部分模板特化触发static_asserts

时间:2017-01-17 05:42:01

标签: c++ c++11

考虑这段代码

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实例化,什么时候不实现?

请注意,我会使用可变参数模板,但无论如何都会发生错误,因此我选择使用最小公分母。

2 个答案:

答案 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>类型,例如charint等:

template <typename T>
struct my_typelist {
    static_assert(delay<int>{}, "");
};

它仍然是fire immediately

那么这里发生了什么?

类模板即使在使用时也不会被隐式实例化,除非它在需要完全定义类型的上下文中使用。

temp.inst/1

  

除非已明确表示类模板特化   实例化([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是类模板特化,其关联的名称空间和类还包括:与模板类型参数(模板模板参数除外)提供的模板参数类型相关联的名称空间和类;任何模板模板参数都是成员的名称空间;以及用作模板模板参数的任何成员模板的类都是成员。 [注意:非类型模板参数不会对相关命名空间的集合产生影响。 - 尾注]

  •   
  • ...

  •