std::conditional_t class 类型与非 class 类型
std::conditional_t for class type vs non-class type
如何解决这个问题:
template<class T>
struct ResultType
{
using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
};
如果 T 不是 class 类型,则它不可能 return 无效,而是:
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>;
所以我不需要尝试调用假表达式,但是如何呢?
在下面:
using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
部分 typename T::result_type
将在 T = int
时失败,因为 typename int::result_type
格式错误。
您可以通过使用模板专业化而不是 std::conditional
来解决此问题,后者做完全相同的事情,但避免在 T
不是 class 类型时执行 T::result_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!");
}
std::conditional_t
是两种类型之间的select,但是当T = int
那么T::result_type
就不是一种类型。你可以使用 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>);
}
失败是因为 std::conditional
选择了 两个类型表达式之一 -- 但此时类型表达式已经被评估。由于 int
不是 class,并且没有 result_type
——它会出错。
正如其他人所指出的,这可以通过 enable_if
或 void_t
使用 SFINAE 解决——但另一种方法是利用表达式 SFINAE 的函数重载,而不是要求部分特化:
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>()));
};
当T
是定义result_type
的类型时,test(const U&)
分支被启用并被选择用于重载解析;否则 test(...)
被选中用于其他所有内容并变为 Default
(在本例中为 void
)。
然后通过评估表达式以查看选择了哪个重载,使用 decltype
推导出类型。
如何解决这个问题:
template<class T>
struct ResultType
{
using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
};
如果 T 不是 class 类型,则它不可能 return 无效,而是:
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>;
所以我不需要尝试调用假表达式,但是如何呢?
在下面:
using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
部分 typename T::result_type
将在 T = int
时失败,因为 typename int::result_type
格式错误。
您可以通过使用模板专业化而不是 std::conditional
来解决此问题,后者做完全相同的事情,但避免在 T
不是 class 类型时执行 T::result_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!");
}
std::conditional_t
是两种类型之间的select,但是当T = int
那么T::result_type
就不是一种类型。你可以使用 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>);
}
失败是因为 std::conditional
选择了 两个类型表达式之一 -- 但此时类型表达式已经被评估。由于 int
不是 class,并且没有 result_type
——它会出错。
正如其他人所指出的,这可以通过 enable_if
或 void_t
使用 SFINAE 解决——但另一种方法是利用表达式 SFINAE 的函数重载,而不是要求部分特化:
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>()));
};
当T
是定义result_type
的类型时,test(const U&)
分支被启用并被选择用于重载解析;否则 test(...)
被选中用于其他所有内容并变为 Default
(在本例中为 void
)。
然后通过评估表达式以查看选择了哪个重载,使用 decltype
推导出类型。