12

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();
Vincent
  • 57,703
  • 61
  • 205
  • 388

3 Answers3

11

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.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    Note that explicitly instantiate `myclass<0>` (`template struct myclass<0>;`) will produce the assert. – Jarod42 Jun 11 '15 at 18:18
  • Pedantic: "you have that guarantee" modulo the usual disclaimer that the standard only requires a diagnostic message when a static assertion fires and does not require the compilation to fail. – T.C. Jun 11 '15 at 18:31
  • @Barry: do you have an example of how I can make compilation fails using resize() on a vector for a type that is not default constructible? – Vincent Jun 11 '15 at 19:11
  • @Vincent Just call `resize(n)` on it for any `n`? – Barry Jun 11 '15 at 19:29
0

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; }
};
KevinZ
  • 3,036
  • 1
  • 18
  • 26
  • This is the right way if you need explicit instantiation, but also a pain because all the other members need to be duplicated. – Ben Voigt Jun 18 '15 at 17:44
  • @BenVoigt True, but I think in practice, class templates that are designed to enforce constraints do not (and should not) contain much else. – KevinZ Jun 19 '15 at 14:59
-2

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.
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • Not necessarily. The usage of enable_if above is incorrect for one thing, as you have to be in a templated context for it to work. Even turning that into a templated member function, enable_if is really for adding/removing something to the candidate set as opposed to always wanting a hard error when attempting to invoke. – Nevin Jun 11 '15 at 19:04
  • This will not compile, your enable if isn't in a context in which SFINAE applies, so it's simply a hard failure. – Barry Jun 11 '15 at 19:50