考虑这段代码
template <typename T>
struct delay : std::false_type{};
template <typename T>
struct my_typelist {
static_assert(delay<T>{}, "");
};
template <typename Tuple>
struct test;
template <typename T>
struct test<my_typelist<T>> {
void pass(){}
};
template <typename T>
void fail(const test<T> &){}
int main()
{
test<my_typelist<int>> t;
t.pass();
fail(t);
}
不调用fail()
代码编译并运行正常。但是,在任何函数中使用t
似乎会触发static_assert
类中的my_typelist
,即使该类从未实例化。虽然这个例子是人为的,但我在std::tuple
中使用不完整类型遇到了同样的问题,尽管我只是使用std::tuple
作为类型列表并且从未实例化它。
为什么static_assert
只在我将变量用作参数时触发,而不是在我调用成员函数时?在什么情况下my_typelist
实例化,什么时候不实现?
请注意,我会使用可变参数模板,但无论如何都会发生错误,因此我选择使用最小公分母。
答案 0 :(得分:9)
对于fail(t)
,如果my_typelist<int>
在其类定义中声明了名为fail
的友元函数,那么argument-dependent lookup将找到该函数(因为{{1} }}是my_typelist<int>
)的关联类。在某些情况下,可以通过重载决策(demo)来选择该朋友而不是全局函数test<my_typelist<int>>
。因此,必须实例化fail
的定义并检查它是否发生。在my_typelist<int>
周围添加括号将取消依赖于参数的查找,并且无需实例化fail
,在这种情况下,my_typelist<int>
不会被触发(demo)。
在static_assert
的情况下,t.pass()
未实例化,因为已知my_typelist<int>
将始终调用t.pass()
的成员函数,并且赢得了test<my_typelist<int>>
受my_typelist<int>
完整性的影响。
答案 1 :(得分:7)
所以,让我们走这条路:
首先从此working code更改您的static_assert
:
template <typename T>
struct my_typelist {
static_assert(delay<T>{}, "");
};
到此:
template <typename T>
struct my_typelist {
static_assert(false, "");
};
它将fire immediately。请注意,false
表达式不依赖于任何类型。
现在,将其更改为不依赖于任何模板参数的delay<T>
类型,例如char
,int
等:
template <typename T>
struct my_typelist {
static_assert(delay<int>{}, "");
};
它仍然是fire immediately。
那么这里发生了什么?
类模板即使在使用时也不会被隐式实例化,除非它在需要完全定义类型的上下文中使用。
除非已明确表示类模板特化 实例化([temp.explicit])或显式专门化 ([temp.expl.spec]),类模板特化是隐式的 在上下文中引用特化时实例化 需要完全定义的对象类型或完整性 类类型会影响程序的语义。
请参阅@cpplearner's answer以了解调用函数fail(...)
实例化my_typelist<int>
的原因。基本上ADL会强制执行此类实例化,您可以使用限定名称::foo
或括号来抑制。
为了完整性:rules中的一个others for ADL(强调我的):
basic.lookup.argdep/2:对于函数调用中的每个参数类型T,有一组零个或多个关联的命名空间以及一组零个或多个关联的类被视为。< / p>
...
basic.lookup.argdep/2.2:如果T是类类型(包括联合),则其关联的类是:类本身;它所属的成员,如果有的话; 及其直接和间接基类。其关联的命名空间 是其关联类的最里面的封闭命名空间。此外,如果T是类模板特化,其关联的名称空间和类还包括:与模板类型参数(模板模板参数除外)提供的模板参数类型相关联的名称空间和类;任何模板模板参数都是成员的名称空间;以及用作模板模板参数的任何成员模板的类都是成员。 [注意:非类型模板参数不会对相关命名空间的集合产生影响。 - 尾注]
...