Basic Problem Statement
I'm learning about SFINAE. I tried an extremely simple enable_if
:
// 1: A foo() that accepts arguments that are derived from Base
template <typename T, typename Enable = enable_if_t<std::is_base_of_v<Base, T>>>
void foo(T thing) {
std::cout << "It's a derived!" << std::endl;
}
// 2: A foo() that accepts all other arguments
template <typename T, typename Enable = enable_if_t<!std::is_base_of_v<Base, T>>>
void foo(T thing) {
std::cout << "It's not a derived." << std::endl;
}
The compiler complains that foo
is multiply defined. The internet tells me that this is because the template arguments aren't relevant when checking function signatures.
Variants I Tried
In my quest to do the most basic metaprogramming possible, I began throwing syntax at the problem. Here's a list of things that I tried for the enable_if
statement (inverted statement i.e. !std::is_base_of
identical, but omitted for brevity):
Anonymous Type, No typename
, Equals 0
https://en.cppreference.com/w/cpp/types/enable_if tells me that what I did above was wrong. But its suggestion (found under the first notes block) is appropriately cryptic, and more importantly, also doesn't compile.
std::enable_if_t<std::is_base_of_v<Base, T>> = 0
Anonymous Type, No typename
, Equals void
Thinking that maybe if I'm programming with types, using a type would be a wise choice, I instead tried to default the template to void
. No dice.
std::enable_if_t<std::is_base_of_v<Base, T>> = void
Anonymous Type, Yes typename
, Equals void
While we're throwing syntax at it, if I'm defaulting this template parameter to a type, shouldn't I use the typename keyword?
typename std::enable_if_t<std::is_base_of_v<Base, T>> = void
What Finally And Oh So Obviously Worked
typename enable_if_t<std::is_base_of_v<Base, T>, T>* = nullptr
I've asked everyone I know why this works yet my other variants don't, and they are equally confused. I'm at a loss. To make matters more confusing, if I name this type (e.g. typename Enable = ...
), it fails to compile.
I would be extremely grateful if one who is more familiar with TMP and enable_if
would explain to me:
- Why does declaring the
enable_if
as a pointer to a type and defaulting it tonullptr
work? - What are the semantic rules for defaulting
enable_if
? - What are the semantic rules for naming types produced by
enable_if
? - Is there a reference I can use which clearly explains this and other rules like it in template-land?
Many thanks.
void
is not a valid value for the non-type template parameter to the left of=
"if I name this type" - it is not a type,typename enable_if_t<std::is_base_of_v<Base, T>, T>*
would be a pointer toT
value that requires a valid pointer value as an initializer, not some type – Improbability