Reputation: 6200
How to fix this:
template<class T>
struct ResultType
{
using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
};
It cannot be it is supposed to return void, if T is not class type, but instead:
error: ‘int’ is not a class, struct, or union type 24 | using type = std::conditional_tstd::is_class_v<T, typename T::result_type, void>;
So I need to not try to invoke the false expression, but how?
Upvotes: 2
Views: 1145
Reputation: 11220
The failure is because std::conditional
selects one of the two type expressions -- but by this point the type expressions are already evaluated. Since int
is not a class, and does not have result_type
-- it errors.
As others have pointed out, this can be resolved with SFINAE via enable_if
or void_t
-- but one other approach is to leverage function overloads with expression SFINAE instead of requiring partial specializations:
template <typename T, typename Default = void>
class ResultType
{
static auto test(...) -> Default;
template <typename U>
static auto test(const U&) -> typename U::result_type;
public:
using type = decltype(test(std::declval<T>()));
};
When T
is a type that defines result_type
, the test(const U&)
branch is enabled and is selected for overload resolution; otherwise test(...)
is selected for everything else and becomes Default
(void
, in this case).
The type is then deduced with decltype
by evaluating the expression to see which overload gets selected.
Upvotes: 0
Reputation: 122458
std::conditional_t
is to select between two types, but when T = int
then T::result_type
is not a type. You can use sfinae:
#include <type_traits>
template <typename T, typename = void>
struct result_type_or_void {
using type = void;
};
template <typename T>
struct result_type_or_void<T,std::void_t<typename T::result_type>> {
using type = typename T::result_type;
};
template<class T>
struct ResultType
{
using type = typename result_type_or_void<T>::type;
};
struct Test {
using result_type = int;
};
int main() {
ResultType<int> t;
static_assert( std::is_same_v<ResultType<int>::type,void>);
static_assert( std::is_same_v<ResultType<Test>::type,int>);
}
Upvotes: 3
Reputation: 3956
In the following:
using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
The part typename T::result_type
will fail when T = int
, because typename int::result_type
is ill-formed.
You can fix this by using a template specialization instead of std::conditional
which does the exact same thing but avoids doing T::result_type
when T
is not a class type:
#include <type_traits>
template <typename T, typename = void>
struct ResultType;
template <typename T>
struct ResultType<T, std::enable_if_t<!std::is_class_v<T>>> {
using type = void;
};
template<typename T>
struct ResultType<T, std::enable_if_t<std::is_class_v<T>>> {
using type = typename T::result_type;
};
// ...
struct X {
using result_type = int;
};
int main() {
static_assert(std::is_same_v<typename ResultType<X>::type, typename X::result_type>, "FAIL!");
static_assert(std::is_same_v<typename ResultType<int>::type, void>, "FAIL!");
}
Upvotes: 7