看起来Clang(3.8)和GNU C ++(4.9)中模板实例化的规则是不一样的。这是一个例子:
#include <cstddef>
template <bool>
class Assert {
Assert(); // private constructor for Assert<false>
};
template <>
class Assert<true> { // implicit public constructor for Assert<true>
};
template <size_t N>
class A {
};
template <class T, size_t N>
T foo(A<N>) {
return T(N - 1);
}
template <class T>
T foo(A<0>) { // foo is not defined for N=0
Assert<false>();
return T(0);
}
int main(int argc, char **argv) {
foo<int>(A<3>());
return 0;
}
此最小示例显示了模板函数foo
,该函数在类型T
和自然数N
上进行了推广。这个函数没有为N=0
定义,所以如果以这种方式使用,我想使用Assert
类来表示编译器错误。
此代码被GNU编译器(以及Visual C ++ 2015)接受,但Clang在“调用类Assert<false>
的私有构造函数”时出错。
那么谁是对的?在我看来,没有foo<T,0>
的调用,所以没有必要实例化这个模板......
编辑:接受Clang对标准的解释,对模板参数实施编译时检查的规范方法是什么?
答案 0 :(得分:5)
我认为clang是正确的,因为Assert<false>
不是依赖类型。
http://en.cppreference.com/w/cpp/language/dependent_name
在模板定义时查找并绑定非依赖名称。即使在模板实例化时存在更好的匹配,此绑定也成立:
不要进行无效的专业化。使它们成为通用目的并使用static_assert(带有依赖值)来检查无效的模板参数类型/值。 static_assert(std::is_same<T, int>::value)
或static_assert(N != 0)
答案 1 :(得分:5)
接受Clang对标准的解释,对模板参数实施编译时检查的规范方法是什么?
您可以删除foo()
A<0>
的“特化”/重载并定义常规模板,如下所示:
template <class T, size_t N>
T foo(A<N>) {
Assert<N != 0>();
return T(N - 1);
}
使用C ++ 11,您无需定义自己的静态Assert,并且可以使用提供的语言static_assert
:
template <class T, size_t N>
T foo(A<N>) {
static_assert(N!=0, "N must be positive");
return T(N - 1);
}
答案 2 :(得分:5)
两个编译器都是正确的。像往常一样,这由[temp.res]/8控制:
知道哪些名称是类型名称允许每个模板的语法 被检查。该程序格式错误,无需诊断,如果:
无法为一个
constexpr if
语句([stmt.if])的模板或子语句生成有效的专门化 模板和模板未实例化,或可变参数模板的每个有效特化都需要一个空模板参数包,或
由于一个不依赖的构造,在其定义之后立即对模板进行假设实例化将是不正确的 在模板参数上,或
在假设实例中对这种结构的解释不同于对此的解释 模板的任何实际实例化中的相应构造。
你的模板违反了第三个要点。
对于正确的解决方案,可以使用合适的static_assert
,也可以明确删除不需要的重载:
template <class T>
T foo(A<0>) = delete;
前者允许更好的错误消息,后者可以更好地与其他元编程一起使用。