【问题标题】:Using std::conditional with is_class<int>, getting compile error将 std::conditional 与 is_class<int> 一起使用,出现编译错误
【发布时间】:2020-04-29 02:22:33
【问题描述】:

我想要一个类型,它依赖于其他具有 X::value_type 的 X 类。 如果其他类中没有这样的typedef,我想自己使用X。

所以我正在寻找类似的代码:

TypeChecker<X>::value_type  // Here, value_type might be X or X::value_type, depending on X.

但我最初的尝试失败了,见下文:

这是一个无法编译的程序:

#include<type_traits>

template<typename T>
struct TypeChecker {
    typedef typename std::conditional<
        std::is_class<T>::value,
        typename T::value_type,  // I believe the error is due to this line (could be wrong)
        T>::type value_type;
};

int main()
{
    TypeChecker<int>::value_type x = 3;

    return 0;
}

它给出:error: 'int' is not a class, struct, or union type

您可以在线尝试:godbolt link

我很困惑,因为我认为std::conditional 会选择正确的分支,但在某种意义上它似乎正在评估它们。至少,足以导致编译错误。

帮助?

【问题讨论】:

    标签: c++ templates c++14 sfinae


    【解决方案1】:

    问题是T::value_type作为模板参数传递给std::conditional,当Tint时,表达式本身无效。

    您可以使用类模板和部分特化来代替。

    template<typename T, typename = void>
    struct TypeChecker {
        typedef T value_type;
    };
    template<typename T>
    struct TypeChecker<T, typename std::enable_if<std::is_class<T>::value>::type> {
        typedef typename T::value_type value_type;
    };
    

    顺便说一句:std::is_class 似乎很松散;您可以将其限制为具有成员类型value_type,例如

    template<typename T, typename = void>
    struct TypeChecker {
        typedef T value_type;
    };
    template<typename T>
    struct TypeChecker<T, std::void_t<typename T::value_type>> {
        typedef typename T::value_type value_type;
    };
    

    【讨论】:

    • 谢谢!未来搜索者的小点:std::void_t 是 C++17(尽管在我的 C++14 环境中实现非常简单)。还有一个很好的youtube video 主题。
    • 嗯,我还认为std::void_t 是在 C++14 中的。可能是因为CWG 1558
    【解决方案2】:

    std::conditional 不会“短路”。要使std::conditional&lt;C,X,Y&gt; 甚至被实例化为类类型,三个模板参数都必须是实际类型。没有办法让模板可以与表示非法或未知类型的模板参数之一一起使用,即使该参数并不直接重要。

    您的TypeChecker的一些解决方案:

    // C++17, using partial specialization SFINAE:
    template <typename T, typename Enable = void>
    struct TypeChecker {
        using value_type = T;
    };
    template <typename T>
    struct TypeChecker<T, std::void_t<typename T::value_type>> {
        using value_type = typename T::value_type;
    };        
    
    // C++11 or later, using function SFINAE:
    template <typename T>
    struct type_identity { using type = T; };
    template <typename T>
    type_identity<typename T::value_type> TypeChecker_helper(int); // not defined
    template <typename T>
    type_identity<T> TypeChecker_helper(...); // not defined
    template <typename T>
    struct TypeChecker {
        using value_type = typename decltype(TypeChecker_helper<T>(0))::type;
    };
    

    (顺便说一句:我总是对自动“解包”东西的模板有点怀疑,比如TypeChecker。它可能会阻碍特别想直接使用容器或迭代器作为值的代码。)

    【讨论】:

    • 感谢您的回答。我同意自动展开有点阴暗。在我的情况下,我有一些适用于vec3vec2vec1 (等)的东西,我希望它也能在普通的float 上合理地工作,就像那是vec1 一样。也许有更好的方法...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多