Your code is valid, and is accepted by clang SVN. The link error was caused by clang bug 17846, which I fixed a couple of days ago.
Can I use a variable template to declare another variable template?
-
02-10-2022 - |
Question
With variable templates coming in C++14 (and Clang already supporting them) and a proposal for standard is_same_v
and likewise type traits, I figured being able to make new type traits as follows would be neat:
template<typename T>
constexpr bool is_const_and_volatile{std::is_const_v<T> && std::is_volatile_v<T>};
Alas, this results in errors equivalent to the following SSCCE (this one contains everything mentioned below):
#include <type_traits>
template<typename T>
constexpr bool is_pointer{std::is_pointer<T>::value};
template<typename T>
constexpr bool foo{is_pointer<T>};
int main() {
//foo<int *>;
}
With the line in main
commented, Clang spits out the following:
warning: variable
is_pointer<type-parameter-0-0>
has internal linkage but is not defined
It looks defined to me (note that changing T
to int *
in foo
works fine). Uncommenting the line in main
to instantiate foo
gives this (again, T
to int *
works fine):
error: constexpr variable
foo<int *>
must be initialized by a constant expression
However, replacing foo
with the following old syntax causes both instances to work fine:
constexpr bool foo{std::is_pointer<T>::value};
Is there something I'm missing about variable templates? Is there a way to build new variable templates with them, or am I forced to use the older syntax to build new ones and only enjoy the syntactic sugar when using them for other code?
Solution
OTHER TIPS
The following seems to work:
#include <type_traits>
#include <iostream>
template<typename T>
struct test {
static constexpr bool is_pointer{std::is_pointer<T>::value};
};
template<typename T>
constexpr bool test<T>::is_pointer;
template<typename T>
constexpr bool foo{test<T>::is_pointer};
int main() {
std::cout << foo<bool>;
std::cout << foo<bool*>;
}
Although it procs the same warning if used in a constexpr
context so I suppose it doesn't really work after all.
// Fail
template<typename T>
typename std::enable_if<foo<T>, void>::type bar()
{
}
int main() {
bar<bool*>();
}
main.cpp:21:5: error: no matching function for call to 'bar'
bar<bool*>();
^~~~~~~~~~
main.cpp:16:45: note: candidate template ignored: substitution failure [with T = bool *]: non-type template argument is not a constant expression
typename std::enable_if<foo<T>, void>::type bar()
It does stop complaining if you give foo
an explicit type:
template<typename T>
typename std::enable_if<foo<bool*>, void>::type bar()
{
}
Or just use test<T>::is_pointer
directly:
template<typename T>
typename std::enable_if<test<T>::is_pointer, void>::type bar()
{
}