8

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?

chris
  • 60,560
  • 13
  • 143
  • 205
  • 2
    Have you tried explicit instantiation? Luc Danton spotted [a bug related to implicit instantiation recently..](http://stackoverflow.com/a/21234048/420683) – dyp Jan 26 '14 at 02:15
  • (Seems to "work" with explicit instantiation: http://coliru.stacked-crooked.com/a/e16d249679a71d0c) – dyp Jan 26 '14 at 02:21
  • @dyp, Ok, but adding one for `bool *` in your example [still gives the linker error](http://coliru.stacked-crooked.com/a/990bc676207518a2), unfortunately, not to mention the fact that I can't possibly explicitly instantiate it for every type. – chris Jan 26 '14 at 02:24
  • I read through http://isocpp.org/files/papers/N3651.pdf. And I think it should work. It looks to me like a clang bug. – user534498 Jan 26 '14 at 03:48
  • @chris I only see warnings in your example. It's also clear that adding explicit instantiations doesn't help suppress the general warning that the primary template `is_pointer` is "not defined". And of course that's only meant to show that there's a relation to implicit instantiation, it's not meant as a solution. – dyp Jan 26 '14 at 12:52
  • @dyp, Sorry, I meant warning. I think it's the wording of the warning making me repeatedly think it's a linker error. – chris Jan 26 '14 at 15:38

2 Answers2

3

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.

Richard Smith
  • 13,696
  • 56
  • 78
0

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

Live Example

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()
{
}