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_ifvoid_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>()));
};

Live Example

T是定义result_type的类型时,test(const U&)分支被启用并被选择用于重载解析;否则 test(...) 被选中用于其他所有内容并变为 Default(在本例中为 void)。

然后通过评估表达式以查看选择了哪个重载,使用 decltype 推导出类型。