I got some problem with a static constexpr member of a template struct. The code compiles but I get linking error. Here's what I'm trying to do:
template<int n>
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
};
template<typename T>
void test(T&& t) {
cout << t << endl;
}
int main() {
test(get<0>(Test<2>::invoke));
return 0;
}
I got linking errors with that, so I tried this:
template<int n>
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
};
// declare it outside the class
template<int n>
constexpr decltype(Test<n>::invoke) Test<n>::invoke;
template<typename T>
void test(T&& t) {
cout << t << endl;
}
int main() {
test(get<0>(Test<2>::invoke));
return 0;
}
But instead I got this strange error:
error: redefinition of 'invoke' with a different type: 'const decltype(Test<n>::invoke)' vs 'const std::tuple<int, const char *, double>'
A different type?? Obviously, the non-template version works just fine:
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
};
constexpr decltype(Test::invoke) Test::invoke;
template<typename T>
void test(T&& t) {
cout << t << endl;
}
int main() {
test(get<0>(Test::invoke));
return 0;
}
How can I get the template version to work? Thank you very much
It looks like you are running into an interesting corner case with decltype, this is covered in the clang bug report Static constexpr definitions used inside template which has the following example with a similar error as yours:
This compiles fine, but when I make the class A, a template like this:
struct L { void operator()() const {} }; template<class X> struct A { static constexpr auto F = L(); }; template<class X> constexpr decltype(A<X>::F) A<X>::F; int main() { A<void>::F(); return 0; }
Clang crashes, if I change the definition line for F to this:
template<class X> constexpr typename std::remove_const<decltype(A<X>::F)>::type A<X>::F;
Then clang produces this error:
error: redefinition of 'F' with a different type constexpr typename std::remove_const<decltype(A<X>::F)>::type A<X>::F; ^ note: previous definition is here static constexpr auto F = L(); ^
and Richard Smith's reply was as follows:
This error is correct. 'constexpr' and 'auto' are red herrings here. Reduced testcase:
template<class X> struct A { static int F; }; template<class X> decltype(A<X>::F) A<X>::F;
Per C++11 [temp.type]p2, "If an expression e involves a template parameter, decltype(e) denotes a unique dependent type." Therefore the type of A::F does not match the type in the template.
the full quote for that from the C++14 draft is as follows:
If an expression e involves a template parameter, decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (14.5.6.1). [ Note: however, it may be aliased, e.g., by a typedef-name. —end note ]
The only obvious way I can see to get this work is:
template<int n>
constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;
No work around was offered in the bug report.
How can I get the template version to work?
FWIW, your method works fine on my desktop, which uses g++ 4.8.4.
You can use:
template<int n>
constexpr decltype(make_tuple(2, "test", 3.4)) Test<n>::invoke;
The following also works on my desktop:
template<int n>
struct Test {
static constexpr auto invoke = make_tuple(2, "test", 3.4);
typedef decltype(invoke) invoke_t;
};
template<int n>
constexpr typename Test<n>::invoke_t Test<n>::invoke;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With