Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template instantiation in GNU C++ and Clang

Looks like the rules for template instantiation in Clang (3.8) and GNU C++ (4.9) are not the same. Here is an example:

#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;
}

This minimal example shows a template function, foo, that is generalized over a type T and a natural number N. This function is not defined for N=0, so I'd like to use the Assert class to signal a compiler error if it is used this way.

This code is accepted by the GNU compiler (and by Visual C++ 2015, as well), but Clang gives an error for "calling a private constructor of class Assert<false>".

So who is right? As I see it, there is no call for foo<T,0>, so there is no need to instantiate this template...

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

like image 609
vukung Avatar asked Nov 20 '16 08:11

vukung


2 Answers

I believe clang is correct, since Assert<false> is not a dependent type.

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:

Don't make specializations that cannot be valid. Make them general purpose and use static_assert (with a dependent value) to check for invalid template argument types/values. static_assert(std::is_same<T, int>::value) or static_assert(N != 0)

like image 101
xaxxon Avatar answered Sep 27 '22 15:09

xaxxon


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

You can drop the "specialization"/overload of foo() for A<0> and define the general template as follows:

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

With C++11 you don't need to define your own static Assert and can use the language provided static_assert:

template <class T, size_t N>
T foo(A<N>) {
  static_assert(N!=0, "N must be positive");
  return T(N - 1);
}
like image 34
Leon Avatar answered Sep 27 '22 16:09

Leon