编译和评估 conditional_t 时出现错误情况的问题

lew*_*wis 7 c++ templates c++17 conditional-types

我很难理解如何阻止代码std::conditional_t在错误分支中被评估。

\n
#include <type_traits>\n\nusing namespace std;\n\nnamespace {\n    template <typename T>\n    using BaseDifferenceType = decltype (T{} - T{});\n}\n\nint main ()\n{\n    using T = int;\n    static_assert(! is_enum_v<T>);\n    BaseDifferenceType<T> xxx {};\n    \n    // PROBLEM IS HERE - though is_enum is false, we still evaluate underlying_type<T> and complain it fails\n    using aaa = conditional_t<is_enum_v<T>, underlying_type_t<T>, BaseDifferenceType<T>>;\n    \n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您可以在https://www.onlinegdb.com/uxlpSWVXr在线尝试此操作。

\n

编译(使用 C++17)给出错误:

\n
#include <type_traits>\n\nusing namespace std;\n\nnamespace {\n    template <typename T>\n    using BaseDifferenceType = decltype (T{} - T{});\n}\n\nint main ()\n{\n    using T = int;\n    static_assert(! is_enum_v<T>);\n    BaseDifferenceType<T> xxx {};\n    \n    // PROBLEM IS HERE - though is_enum is false, we still evaluate underlying_type<T> and complain it fails\n    using aaa = conditional_t<is_enum_v<T>, underlying_type_t<T>, BaseDifferenceType<T>>;\n    \n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Ded*_*tor 7

答案很简单:
你不知道。

std::conditional_t始终具有三个完全评估的参数:
类似布尔值的参数和两种类型。

如果选择其中一个无法评估另一个,则需要使用自定义模板并对其进行适当的专门化:

template <class T, bool = std::is_enum_v<T>>
struct type_finder { using type = std::underlying_type_t<T>; };
template <class T>
struct type_finder<T, false> { using type = BaseDifferenceType<T>; };
template <class T>
using type_finder_t = typename type_finder<T>::type;
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用if constexpr自动返回类型扣除:

template <class T>
auto type_finder_f() {
    if constexpr (std::is_enum_v<T>)
        return std::underlying_type_t<T>();
    else
        return BaseDifferenceType<T>();
}
template <class T>
using type_finder_t = decltype(type_finder_f<T>());
Run Code Online (Sandbox Code Playgroud)


max*_*x66 5

可能的替代方法是创建自定义类型特征,如下所示

template <template <typename...> class Cont, typename ... Ts>
struct LazyType
 { using type = Cont<Ts...>; };
Run Code Online (Sandbox Code Playgroud)

并重写你的std::conditional如下

using aaa = std::conditional_t<
               std::is_enum_v<T>,
               LazyType<underlying_type_t, T>,
               LazyType<BaseDifferenceType, T>>::type;
// ............................................^^^^^^
Run Code Online (Sandbox Code Playgroud)

观察我添加了一个final ::type:它是所需类型的“构造函数”,完全避免了不需要的类型。

题外话未经请求的建议:避免

using namespace std;
Run Code Online (Sandbox Code Playgroud)

这被认为是不好的做法