Reputation: 1884
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?
Upvotes: 7
Views: 1111
Reputation: 137310
Both compilers are correct. As usual, this is controlled by [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, orevery 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.
Your template runs afoul of the third bullet point.
As to the correct solution, either a suitable static_assert
can be used, or you can explicitly delete the undesirable overload:
template <class T>
T foo(A<0>) = delete;
The former allows better error messages, the latter plays more nicely with other metaprogramming.
Upvotes: 5
Reputation: 19761
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)
Upvotes: 5
Reputation: 32454
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);
}
Upvotes: 5