GNU C++ 和 Clang 中的模板实例化

Template instantiation in GNU C++ and Clang

看起来 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 class 来发出编译器错误信号。

此代码已被 GNU 编译器(以及 Visual C++ 2015)接受,但 Clang 给出 "calling a private constructor of class Assert<false>".

的错误

那么谁是对的?正如我所见,没有调用 foo<T,0>,因此无需实例化此模板...

编辑:接受 Clang 对标准的解释,对模板参数强制执行编译时检查的规范方法是什么?

我相信 clang 是正确的,因为 Assert<false> 不是依赖类型。

http://en.cppreference.com/w/cpp/language/dependent_name

Non-dependent names are looked up and bound at the point of template definition. This binding holds even if at the point of template instantiation there is a better match:

不要进行无效的专业化。将它们设为通用并使用 static_assert(具有相关值)来检查无效的模板参数 types/values。 static_assert(std::is_same<T, int>::value)static_assert(N != 0)

Accepting Clang's interpretation of the standard, what is a canonical way to enforce compile-time checks on template parameters?

您可以为 A<0> 删除 foo() 的 "specialization"/重载,并定义通用模板如下:

template <class T, size_t N>
T foo(A<N>) {
  Assert<N != 0>();
  return T(N - 1);
}

使用 C++11,您无需定义自己的静态断言,可以使用提供的语言 static_assert:

template <class T, size_t N>
T foo(A<N>) {
  static_assert(N!=0, "N must be positive");
  return T(N - 1);
}

两个编译器都是正确的。像往常一样,这由 [temp.res]/8:

控制

Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement ([stmt.if]) within a template and the template is not instantiated, or

  • every valid specialization of a variadic template requires an empty template parameter pack, or

  • a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or

  • the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the template.

您的模板与第三个要点冲突。

至于正确的解决方案,可以使用合适的static_assert,或者您可以显式删除不需要的重载:

template <class T>
T foo(A<0>) = delete;

前者允许更好的错误消息,后者更适合其他元编程。