g++ 和 clang++ 具有完整模板参数的不同行为
我有以下 C++11 代码.
I have the following C++11 code.
#include <type_traits>
using IntType = unsigned long long;
template <IntType N> struct Int {};
template <class T>
struct is_int : std::false_type {};
template <long long N>
struct is_int<Int<N>> : std::true_type {};
int main()
{
static_assert (is_int<Int<0>>::value, "");
return 0;
}
Clang++ 3.3 编译代码但在 g++ 4.8.2 上静态断言失败
Clang++ 3.3 compiles the code but on g++ 4.8.2 static assertion fails
$ g++ -std=c++11 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:15:5: error: static assertion failed:
static_assert (is_int<Int<0>>::value, "");
^
$
问题是由不同的积分模板参数引起的.在这种情况下哪个编译器是正确的?
The problem is caused by different integral template parameters. Which compiler is right in this case?
推荐答案
惊喜
这是一个微妙的 Clang 错误,深埋在标准中.问题是在几乎所有情况下,非类型模板arguments都可以转换为模板parameter的类型.例如.表达式 Int<0>
有一个值 0
的 int
文字参数,该参数被转换为类型 unsigned long long模板参数
N
的code>.
The surprise
This is a subtle Clang bug, deeply buried in the Standard. The problem is that in almost all cases, non-type template arguments can be converted to the type of the template parameter. E.g. the expression Int<0>
has an int
literal argument of value 0
that is being converted to the type unsigned long long
of the template parameter N
.
14.8.2 模板参数推导 [temp.deduct]/2 2nd bullet
-- 非类型参数必须匹配对应非类型的类型模板参数,或必须可转换为14.3.2 中指定的相应非类型参数,否则类型推导失败.
-- Non-type arguments must match the types of the corresponding non-type template parameters, or must be convertible to the types of the corresponding non-type parameters as speci?ed in 14.3.2, otherwise type deduction fails.
由于您的类模板 is_int<T>
具有部分特化,我们需要查看
Since your class template is_int<T>
has a partial specialization, we need to look at
14.5.5.1 类模板偏特化的匹配[temp.class.spec.match]
1 当类模板用于需要类的实例化,有必要确定是否实例化将使用主模板或其中之一生成部分专业.这是通过匹配模板来完成的带有模板的类模板特化的参数部分特化的参数列表.
1 When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
2 部分特化匹配给定的实际模板参数列出偏特化 的模板参数是否可以从实际模板参数列表(14.8.2)推导出来.
2 A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list (14.8.2).
所以看起来我们可以继续前面提到的 14.8.2/2 2nd bullet 并匹配第二个专业化(尽管在这种情况下必须玩更复杂的重载决议游戏).
So it would seem that we can proceed to the earlier quote of 14.8.2/2 2nd bullet and match the second specialization (although in that case an even more complicated overload resolution game would have to be played).
然而,事实证明(正如@DyP 在评论中提到的那样)标准中的另一个条款取代了这一点:
However, it turns out (as mentioned by @DyP in the comments) that another clause in the Standard supersedes this:
14.8.2.5 从类型 [temp.deduct.type] 推导出模板参数
17 如果,在一个非类型的函数模板的声明中模板参数,非类型模板参数用于函数参数列表中的表达式,如果对应推导出模板参数,模板参数类型应匹配模板参数的类型,除了一个从数组绑定推导出的模板参数可以是任何整数类型.[ 例子:
17 If, in the declaration of a function template with a non-type template-parameter, the non-type templateparameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type. [ Example:
template<int i> class A { / ... / };
template<short s> void f(A<s>);
void k1() {
A<1> a;
f(a); // error: deduction fails for conversion from int to short
f<1>(a); // OK
}
结果是无法推断出 is_int
的部分特化,因为它不采用完全相同的类型(unsigned long long
vs long long
) 作为 Int
类模板的正式非类型模板参数.
The upshot is that the partial specialization of is_int
cannot be deduced because it does not take the exact same type (unsigned long long
vs long long
) as the Int
class template's formal non-type template parameter.
您可以通过在 is_int
的部分特化中赋予非类型模板参数 N
与非类型参数 N<相同的类型来解决此问题/code> 在主模板
Int
.
You can resolve this by giving the non-type template parameter N
in the partial specialization of is_int
the same type as the non-type parameter N
in the primary template Int
.
template <IntType N>
// ^^^^^^^^
struct is_int<Int<N>> : std::true_type {};
实例.
相关文章