我有以下C ++ 11代码。
#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静态断言失败
$ 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, "");
^
$
问题是由不同的整数模板参数引起的。 在这种情况下哪个编译器是正确的?
答案 0 :(得分:25)
这是一个微妙的Clang bug,深深埋藏在标准中。问题在于,几乎在所有情况下,非类型模板参数都可以转换为模板参数的类型。例如。表达式Int<0>
具有值为int
的{{1}}字面值参数,该参数将转换为模板参数0
的{{1}}类型。
14.8.2模板参数演绎[temp.deduct] / 2 2nd bullet
- 非类型参数必须与相应的非类型的类型匹配 模板参数,或必须可转换为的类型 相应的非类型参数,如14.3.2中规定的,否则 类型扣除失败。
由于您的班级模板unsigned long long
具有部分专业化,我们需要查看
14.5.5.1匹配类模板部分特化[temp.class.spec.match]
1在需要的上下文中使用类模板时 实例化该类,有必要确定是否 实例化将使用主模板或其中一个生成 部分专业化。 这是通过匹配模板来完成的 使用模板的类模板特化的参数 部分专业化的参数列表。
2部分特化与给定的实际模板参数匹配 列出部分特化的模板参数是否可以 从实际模板参数列表推导出来(14.8.2)。
所以看起来我们可以继续使用早期的14.8.2 / 2第2个子弹的引用并匹配第二个特殊化(尽管在这种情况下,必须进行更复杂的重载分辨率游戏)。
然而,事实证明(正如@DyP在评论中所提到的),标准中的另一个条款取代了这一点:
14.8.2.5从类型[temp.deduct.type]中扣除模板参数
17如果,在声明具有非类型的函数模板中 template-parameter,非类型模板参数用于 函数参数列表中的表达式,如果相应的话 推导出template-argument,模板参数类型必须匹配 模板参数的类型完全,除了a 从数组绑定推导出的模板参数可以是任何积分 类型。 [例如:
N
结果是is_int<T>
的部分特化无法推断,因为它不像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
vs unsigned long long
)形式非类型模板参数。
您可以通过在long long
的部分特化中为非主要模板中的非类型参数Int
提供非类型模板参数N
来解决此问题。 1}}。
is_int
答案 1 :(得分:10)
Clang不一致。 Since it accepts your code,我希望以下代码必须输出f(Int<long long>)
而不是f(T)
:
using IntType = unsigned long long;
template <IntType N> struct Int {};
template<typename T>
void f(T) { std::cout << "f(T)" << std::endl; }
template<long long N>
void f(Int<N>) { std::cout << "f(Int<long long>)" << std::endl; }
int main()
{
f(Int<0>{});
}
但令人惊讶的是,它输出了这个(online demo):
f(T)
这表明Int<0>
与接受参数Int<N>
的第二个重载不匹配。如果是这样,那么当它被用作类模板的模板参数(在你的情况下)时,为什么它与Int<N>
匹配?
我的结论:
无论哪种方式,Clang似乎都有bug。
另一方面,海湾合作委员会至少是一致的。这并不能证明它没有bug - 这可能意味着它在两种情况下都有bug!除非有人提出标准并且显示它还有 错误,否则在这种情况下我会信任GCC。