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

时间:2016-11-20 08:57:22

标签: c++ templates gcc instantiation 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类来表示编译器错误。

此代码被GNU编译器(以及Visual C ++ 2015)接受,但Clang在“调用类Assert<false>的私有构造函数”时出错。

那么谁是对的?在我看来,没有foo<T,0>的调用,所以没有必要实例化这个模板......

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

3 个答案:

答案 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;

前者允许更好的错误消息,后者可以更好地与其他元编程一起使用。