I have a function that I want to toggle on and off using std::enable_if
on its return type. Like so:
#include <utility>
#include <type_traits>
template<typename T>
auto bob(T v) {
return v.size;
//return 0;
}
template<typename T>
struct the_type {
using type = decltype( bob( std::declval<T>() ) )>;
};
template<int I>
auto foo() -> std::enable_if_t<I != 0, typename the_type<int>::type> {
return {};
}
template<int I>
auto foo() -> std::enable_if_t<I == 0, int> {
return 5;
}
int main() {
foo<0>();
}
However the problem I am having is that even when the predicate of std::enable_if
is false
it still tries to instantiate the type parameter, which in turn invokes another function using decltype
. That function does not work with specified type.
Please note the above code is a test case that demostrates the same problem I am having with a much large code base.
I've tried adding in std::conditional_t
but that like std::enable_if
tries to instantiate both of its template parameters.
The behaviour I'd like is for the compiler to recognise that 0 != 0
is false, and either not instantiate the type parameter or simply ignore any compile errors.
How can I resolve this? Thanks in advance.
CodePudding user response:
If you put the enable_if
in a different place it will work better:
template<int I, typename = std::enable_if_t<I != 0>>
typename the_type<int>::type foo()
CodePudding user response:
Ok, I've found a work around, but I'd still be keen to hear other ideas, as this feels crusty:
I can change the_type
to the following:
template<typename T, bool = std::is_same_v<T, int>>
struct the_type {
using type = decltype( bob( std::declval<T>() ) );
};
template<typename T>
struct the_type<T, true> {
using type = std::nullptr_t;
};
Note in the real code, std::is_same
is replace by a more complex predicate - using std::is_same
to demonstrate the behaviour.
In an ideal world, I wouldn't need to provide the predicate, perhaps the failure of bob would be enough for it to try something else.
CodePudding user response:
Your have several similar issue:
bob
is not SFINAE friendly. i.ebob<int>
produce an hard failure (failure happens in the body, not in declaration).the_type
is not SFINAE friendly neither but anywaythe_type<int>::type
doesn't depend of template parameter (so error doesn't come from substitution).
You can delay instantiation to avoid hard error
template<typename T>
auto bob(T v) { return v.size; }
template <typename T>
struct the_type {
using type = decltype( bob( std::declval<T>() ) )>;
};
template<int I>
auto foo()
-> typename std::conditional_t<I == 0,
std::type_identity<int>,
the_type<int>
>::type
{
if constexpr (I == 0) {
return 5;
} else {
return {};
// Following would still be an hard error (independent of I)
// return typename the_type<int>::type{};
}
}
Demo
Notice that foo<1>
would still be an hard error.
Or make all type/function SFINAE friendly:
template<typename T>
auto bob(T v) -> decltype(v.size)
{
return v.size;
}
template<typename T, typename Enabler = void>
struct the_type;
template<typename T>
struct the_type<T, std::void_t<decltype( bob( std::declval<T>() ) )>>
{
using type = decltype( bob( std::declval<T>() ));
};
template <int I, typename T>
struct always
{
using type = T;
};
template <int I, typename T>
using always_t = typename always<I, T>::type;
template<int I>
auto foo()
-> std::enable_if_t<I != 0, typename the_type<always_t<I, int>>::type> {
return {};
}
template<int I>
auto foo() -> std::enable_if_t<I == 0, int> {
return 5;
}