std::conditional_t 用于类类型与非类类型

时间:2021-08-01 17:58:45

标签: c++ typetraits

如何解决这个问题:

template<class T>
struct ResultType
{
    using type = std::conditional_t<std::is_class_v<T>, typename T::result_type, void>;
};

如果 T 不是类类型,那么它不能应该返回 void,而是:

<块引用>

错误:'int' 不是类、结构或联合类型 24 | using type = std::conditional_tstd::is_class_v;

所以我不需要尝试调用错误表达式,但是如何调用?

3 个答案:

答案 0 :(得分:6)

以下内容:

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::result_type 不是类类型时避免执行 T

#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!");
}

答案 1 :(得分:3)

std::conditional_t 是在两种类型之间进行选择,但是当 T = intT::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>);
}

答案 2 :(得分:0)

失败是因为 std::conditional selects 两个类型表达式之一——但此时类型表达式已经被评估。由于 int 不是一个类,并且没有 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 推导出类型。