static_assert depend on class template

471 views Asked by At

Consider the following code:

template <unsigned int N>
struct myclass
{
    unsigned int f() {return N;}
    unsigned int g() {static_assert(N > 0, ""); return N-1;}
};

Question: Do I have the guarantee that the following code will compile:

myclass<0> c;
c.f();

But the following will not:

myclass<0> c;
c.f();
c.g();
3

There are 3 answers

4
Barry On BEST ANSWER

Yes, you have that guarantee. From [temp.inst]/11, emphasis mine:

An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, or a static data member of a class template that does not require instantiation.

If you don't call g(), it doesn't require instantiation, so there should be no issues calling myclass<0>{}.f().

This is the same guarantee that lets you use std::vector and std::map with types that aren't default constructible as long as you don't do things like call resize() and operator[], respectively.

A followup, as Jarod42 points out, is that explicitly instantiating myclass<0> will produce the assert because, from [temp.explicit]/8:

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes and members that are templates) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below.

The exceptions don't apply here.

2
Guillaume Racicot On

If you wish to disable a function at compiler time, you should use enable_if instead of static_assert

template<unsigned int N>
struct Foo {
    unsigned int f() {}
    enable_if_t<(N > 0), unsigned int> g() {}
};

Foo<0> t{};
t.f(); /// this is okay and will compile
// t.g() if I uncomment this line, it will not compile anymore.
2
KevinZ On

Use a template specialization:

template <unsigned N>
struct myclass
{
    unsigned f () { return N; }
    unsigned g () { return N-1; }
};

template <>
struct myclass <0>
{
    unsigned f () { return 0; }
};