4

I was wondering about the advantage of std::enable_if over static_asserts to prevent template instantiation. This answer suggests, that std::enable_if allows SFINAE, which is a convincing argument in the case of function templates.

However is this argument legitimate for class templates (and variable templates)? As far as I know, there is no overload resolution involved there, making SFINAE - again, as far as I know - not relevant, but I might be wrong. If so, can you name an example?

If not, I assume static_assert to be the better solution for the given issue (preventing template instantiation) in the case of class templates, since it's arguably more explicit, concise and readable and allows a custom error message. Is that correct or am I missing a point other than SFINAE?

Reizo
  • 1,374
  • 12
  • 17

3 Answers3

5

Handling a few distinct types can be done with specialising as you said:

template <class>
class foo;

template <>
class foo <int> { /* int implementation */ };

template <>
class foo <char> { /* char implementation */ };

Now consider that we want to specialise multiple types, and we want float and double to be in the same specialisation. We can't do this without SFINAE, so to avoid repeating the implementation we use inheritance:

class foo_fp_implementation {
    /* specialise for all floating point */ 
};

template <>
class foo <float> : foo_fp_implementation {};

template <>
class foo <double> : foo_fp_implementation {};

So far we've avoided SFINAE, but what if you want a specialisation that takes all types with a certain interface? Example: has method int bar ()? We could try to use the above trick and list all the possible types, but what if there are too many to maintain, or you want it to be usable by others who may use their own types. Here SFINAE comes to the rescue:

template <class, class = int>
class foo;

template <class T>
class foo <T, decltype(T().bar())> {
    // implement using bar interface
};

// other specialisations...

In the above situation, static_assert simply couldn't help.


In conclusion, SFINAE helps to specialise based off exact behaviour rather than exact types.

Elliott
  • 2,603
  • 2
  • 18
  • 35
  • "clearer and simpler" (in case of no overloading) *and* does not induce some disadvantage similar to preventing SFINAE in function overload resolution, correct? – Reizo Aug 31 '20 at 11:31
  • Nvm, I realized I am measuring with double standards with my previous comment. There is neither a disadvantage in using `static_assert` in function template *if* I am not aiming for overloading. So the analogy of class and function template is quite complete. – Reizo Aug 31 '20 at 11:34
  • @Reizo, yes, I was a little confused by your question. If you use `static_assert` instead of `enable_if` then it will prevent you from doing overloading (based on that template alone [you can still overload based on other args/templates]) – Elliott Aug 31 '20 at 11:38
3

However is this argument legitimate for class templates (and variable templates)? As far as I know, there is no overload resolution involved there, making SFINAE - again, as far as I know - not relevant, but I might be wrong. If so, can you name an example?

You can specialise class templates and SFINAE may be used to pick between specialisations. It will also prevent instantiation of such (by then, possibly ill-formed) class / its specialisation, instead of failing to compile due to static_assert.

Fureeish
  • 12,533
  • 4
  • 32
  • 62
  • I am unaware of how a template type specification may be ambiguous, requiring to ' pick between [possible] specializations'. Can you please add an example to your answer? – Reizo Aug 31 '20 at 11:05
0

std::enabled_if is used in SFIANE for specialization of template classes, methods and .... static_assert is used for checking a contract at compile time, and providing a useful error message.

apramc
  • 1,346
  • 1
  • 15
  • 30