Trillian
Trillian

Reputation: 6437

How can I make a templated variable specialization fail at compile time if instantiated?

Is it possible to get a compile-time error if and only if the default specialization of a templated variable gets instantiated? For example

template<typename T>
constexpr int foo = /* Something that fails */;

template<>
constexpr int foo<bool> = 42;

// ...
int bar = foo<bool>; // All good!
int meow = foo<int>; // Error if and only if this line exists

All I've tried putting in /* Something that fails*/ has ended up failing even if the specialization isn't instantiated. Is this possible? Even better if the error can somehow be reported through a mechanism like static_assert so that it is at least somewhat legible.

Upvotes: 2

Views: 90

Answers (3)

zneak
zneak

Reputation: 138051

You should ask a language lawyer if this is standard. Clang will not let you leave a templated constexpr variable undefined, but it will let you reference undefined template instantiations from a constexpr initializer. You can then write this:

template<typename T>
struct no_such_type_for_foo;

template<typename T>
constexpr int foo = no_such_type_for_foo<T>::value;

template<>
constexpr int foo<int> = 4;

int main()
{
    int y = foo<int>; // all good
    int z = foo<bool>; // implicit instantiation of undefined template 'no_such_type_for_foo<bool>'
}

Upvotes: 4

Trillian
Trillian

Reputation: 6437

Based on zneak and Sam's solutions, I came up with a variant that allows a custom error message through static_assert. The key is that the static_assert condition needs to depend on the template argument, or it will be evaluated immediately whether or not the template is actually used.

The problem is that we want the static_assert to unconditionally fail, and so the condition should reduce to false for every possible argument. We are relying on the compiler not making that analysis itself (I'm not sure whether it would actually be allowed to figure it out if the template is not instantiated).

template<typename T>
constexpr int no_such_type_for_foo()
{
    static_assert(sizeof(T) < 0, "No such type for foo");
    return 0;
}

template<typename T>
constexpr int foo = no_such_type_for_foo<T>();

template<>
constexpr int foo<bool> = 42;

int main()
{
    int y = foo<bool>; // all good
    int z = foo<int>; // static_assert failed "No such type for foo"
}

Upvotes: 0

Sam Varshavchik
Sam Varshavchik

Reputation: 118340

gcc doesn't like the static keyword in the template instantiation.

But just leaving the default template undefined appears to do the trick:

template<typename T>
constexpr int foo;

template<>
constexpr int foo<bool> = 42;

With that, this works:

std::cout << foo<bool> << std::endl;

and this fails:

std::cout << foo<char> << std::endl;

with:

t.C:2:15: error: uninitialized const ‘foo<char>’ [-fpermissive]
 constexpr int foo;
               ^

I don't see much difference between this situation, and a more common situation where the default template is not defined:

template<typename T> class foo;

template<>
class foo<char> {

// ...
};

Same thing.

Upvotes: 1

Related Questions