Reputation: 1044
I've been using enable_if
in this approximate manner with various versions of GCC (up to 5.2):
template< bool b, std::enable_if_t< b >... >
void fn() { std::cout << 1 << std::endl; }
template< bool b, std::enable_if_t< !b >... >
void fn() { std::cout << 2 << std::endl; }
// ...
fn< true >();
fn< false >();
But, as it turns out, Clang 3.7 does not accept this ("call to 'fn' is ambiguous").
Q1. Who's right, and why?
There are, of course, other ways to do it, but I kind of don't like
template< bool b >
std::enable_if_t< b, void > fa() { std::cout << 1 << std::endl; }
// ...
and its ilk for making normal parts of the function signature less readable, and
template< bool b, std::enable_if_t< b, int > = 0 >
void fd() { std::cout << 1 << std::endl; }
// ...
for involving irrelevant elements (types and values).
Q2. What other (correct, more readable, less hackish/weird) ways to use enable_if
/enable_if_t
are there?
Upvotes: 5
Views: 179
Reputation: 69864
Q2. What other (correct, more readable, less hackish/weird) ways to use enable_if/enable_if_t are there?
arguably, this is more readable and less hackish?
#include <iostream>
#include <type_traits>
template< bool b >
auto fn() -> std::enable_if_t<b>
{
std::cout << 1 << std::endl;
}
template< bool b>
auto fn() -> std::enable_if_t<!b>
{
std::cout << 2 << std::endl;
}
// ...
auto main() -> int
{
fn< true >();
fn< false >();
return 0;
}
and here's another way which could be thought of as more expressive:
#include <iostream>
#include <type_traits>
template <bool b> using When = std::enable_if_t<b, bool>;
template <bool b> using Unless = std::enable_if_t<!b, bool>;
template< bool b, When<b> = true>
void fn2()
{
std::cout << 1 << std::endl;
}
template< bool b, Unless<b> = true>
void fn2()
{
std::cout << 2 << std::endl;
}
auto main() -> int
{
fn2< true >();
fn2< false >();
return 0;
}
... or perhaps something like this is more expressive?
template <bool b> using Eval = std::integral_constant<bool, b>;
template<bool b>
void fn3()
{
struct fn3_impl
{
static void when(std::true_type)
{
std::cout << 1 << std::endl;
}
static void when(std::false_type)
{
std::cout << 2 << std::endl;
}
};
fn3_impl::when(Eval<b>());
}
Upvotes: 1
Reputation: 477010
I'm not sure I'd use enable_if
at all here. You're not trying to constrain an overload set, so I'd call that counter-idomatic.
Simple specialization seems to work just fine:
template <bool> void fn();
template <> void fn<true>() { std::cout << "true fn\n"; }
template <> void fn<false>() { std::cout << "false fn\n"; }
Upvotes: 3
Reputation: 42899
According to the standard 14.1/p7 Template parameters [temp.param] (Emphasis Mine):
A non-type template-parameter shall not be declared to have floating point, class, or void type.
Consequently, your code snippet is ill-formed. Thus, GCC is wrong on this.
However if you change to:
template< bool b, std::enable_if_t< b, int>... >
void fn() { std::cout << 1 << std::endl; }
template< bool b, std::enable_if_t< !b, int>... >
void fn() { std::cout << 2 << std::endl; }
Restriction is lifted, and this code is legitimate and should be accepted. Apparently, it seems that Clang rejects this code as well. IMHO, this is a Clang bug.
As I found out a similar bug has been reported 23840.
Now for the practical part, I don't know if this is practical/less hackish/less weird but you could do the following:
template< bool b, std::enable_if_t< b, int> = 0 >
void fn() { std::cout << 1 << std::endl; }
template< bool b, std::enable_if_t< !b, int> = 0 >
void fn() { std::cout << 2 << std::endl; }
Upvotes: 3