从上一个问题的接受答案我发现了一个规则我不知道模板和格式良好
程序格式错误,不需要诊断,如果:
- [...]
- 可变参数模板的每个有效特化都需要一个空的模板参数包,或者
- [...]
根据这个规则(如果我理解正确的话),下面的模板函数是病式的
template <typename ... Ts>
int foo (std::tuple<Ts...> const &)
{ return std::get<sizeof...(Ts)>(std::tuple<int>{42}); }
因为唯一有效的专业化要求和空
Ts...
参数包。
但是(也许是因为我不太懂英语)如果模板有两个或多个参数包,我不确定是否理解这个规则。
我的意思是...下面的
foo()
函数
#include <tuple>
#include <iostream>
template <typename ... Ts, typename ... Us>
int foo (std::tuple<Ts...> const &, std::tuple<Us...> const &)
{ return std::get<sizeof...(Ts)+sizeof...(Us)-1U>(std::tuple<int>{42}); }
int main ()
{
auto t0 = std::tuple<>{};
auto t1 = std::tuple<int>{0};
//std::cout << foo(t0, t0) << std::endl; // compilation error
std::cout << foo(t0, t1) << std::endl; // print 42
std::cout << foo(t1, t0) << std::endl; // print 42
//std::cout << foo(t1, t1) << std::endl; // compilation error
}
是良式还是病式?
因为它的有效特化要求
Ts...
or Us...
为空(并且其他参数包的大小正好为 1)。
如果有一个必须永远为空的空参数包(所以我的示例应该是格式正确的,因为两个参数包都不能为空),或者在某种意义上,该规则应该被解释为程序格式错误如果在每个专业化中至少有一个空参数包,并且在每个专业化中都不一定相同(所以我的例子应该是错误的),则格式错误?
我认为标准措辞没有清楚地传达规则的意图,也许永远不会。
“无有效特化”规则旨在允许编译器对语法有效但在语义上被视为无意义的结构进行早期诊断,而无需实例化模板。这是一个例子:
template <int x>
void foo() {
typedef double D;
x.~D();
}
这在语法上没有任何错误。从语义上讲,这是试图破坏一个
int
类型的值,就好像它是double
类型一样,这当然是不允许的。如果您实例化foo
,则允许编译器对其进行诊断,但不是必需的。似乎 Clang 给出了诊断,而 GCC 没有。
同样,关于模板参数包的规则的目的是,如果包扩展的模式在语义上是无意义的,那么允许编译器及早诊断它。上述示例的一个变体说明了这一点:
template <int... x>
void foo() {
typedef double D;
(x.~D(), ...);
}
唯一合式的方法是如果在展开式中有 0 个
x.~D()
的实例;即使是 1 也总是病式的。所以这里的想法是允许(但不是必需)编译器诊断格式错误的模式,即使可以通过提供一个导致扩展中 0 个元素的空包来获得格式正确的专业化。
(同样,GCC 对此没问题,甚至可以让您调用
foo<>()
。即使未实例化,Clang 也会对其进行诊断。)
据此,有两个参数包时的规则应该为:
sizeof...
,那么关于每个需要空包的有效专业化的规则甚至根本不适用。这不符合当前规则的“精神”。