部分模板专用化触发static_assels

Partial template specialization triggering static_asserts

本文关键字:static assels 专用      更新时间:2023-10-16

考虑这个代码

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似乎都会触发my_typelist类中的static_assert,即使该类从未实例化。尽管这个例子是人为设计的,但我在std::tuple中使用不完整的类型时遇到了同样的问题,尽管我只是将std::tuple用作类型列表,并且从未实例化过它

为什么static_assert只在我将变量用作参数时触发,而不是在我调用成员函数时触发?my_typelist是在什么情况下实例化的,什么时候不实例化?

请注意,我本来会使用可变模板,但不管怎样,错误都会发生,所以我选择使用最低公分母。

fail(t)的情况下,如果my_typelist<int>在其类定义中声明了一个名为fail的友元函数,则该函数将通过参数相关查找找到(因为my_typelist<int>test<my_typelist<int>>的关联类)。在某些情况下,可以通过过载分辨率(demo)来选择该朋友,而不是全局函数fail。因此,必须实例化并检查my_typelist<int>的定义,看看是否会发生这种情况。在fail周围添加括号将抑制与参数相关的查找,并消除实例化my_typelist<int>的需要,在这种情况下,static_assert不会被触发(演示)。

t.pass()的情况下,my_typelist<int>不被实例化,因为已知t.pass()将始终调用test<my_typelist<int>>的成员函数,并且这不会受到my_typelist<int>的完整性的影响。

所以,让我们走这条路:

首先从以下工作代码更改您的static_assert

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

到此:

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

它马上就要着火了。请注意,false表达式不依赖于任何类型。


现在,将其更改为不依赖于任何模板参数的delay<T>类型,例如charint等:

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

它仍然会立即起火。


这里发生了什么?

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

temp.inst/1

除非类模板专门化已明确实例化的(〔temp.explicit〕)或显式专用的([temp.exp.spec]),类模板专门化是隐式的在上下文中引用专门化时实例化需要一个完全定义的对象类型,或者当类类型会影响程序的语义。

请参阅@cpplearner的答案,了解为什么调用函数fail(...)会实例化my_typelist<int>。基本上,ADL会强制执行这样的实例化,您可以使用限定名称::foo或括号来抑制。

完整性:在ADL的一个规则中(强调矿):

basic.lookup.argdep/2:对于函数调用中的每个参数类型T,都有一组零个或多个关联的命名空间和一组零或多个要考虑的关联类。

  • 。。。。

  • basic.lookup.argdep/2.2:如果T是一个类类型(包括并集),则其关联的类为:类本身;其所属类别(如有);及其直接和间接基类。其关联的命名空间是其关联类的最里面的封闭命名空间。此外,如果T是类-模板专用化,其关联的命名空间和类还包括:与为模板类型参数(不包括模板模板参数)提供的模板参数的类型相关联的命名空间和类别;任何模板模板参数都是其成员的命名空间;以及用作模板模板参数的任何成员模板都是其成员的类[注意:非类型模板参数对关联的命名空间集没有贡献。—尾注]

  • 。。。。