【问题标题】:enable conversion operator using SFINAE使用 SFINAE 启用转换运算符
【发布时间】:2014-12-11 21:52:57
【问题描述】:

我正在尝试使用 SFINAE 重载 operator T(),以在 T 是基本类型时返回副本,并在 T 是类时返回 const 引用。

在下面的示例中使用double 时,我无法删除第二个重载(使用std::is_class)。

也就是说,我得到的错误是:

error: no type named ‘type’ in ‘struct std::enable_if<false, const double&>’
operator typename std::enable_if< std::is_class<T>::value, const T&>::type () const
^

我做错了什么?

#include <iostream>
#include <type_traits>

template<typename T>
struct Foo
{
    operator typename std::enable_if<!std::is_class<T>::value, T >::type () const
    {
        return _val;
    }

    operator typename std::enable_if< std::is_class<T>::value, const T&>::type () const
    {
        return _val;
    }

    T _val;
};

int main()
{
    Foo<double> f1;
    f1._val = 0.3;

    double d = f1;
    std::cout << d << std::endl;
    return 0;
}

【问题讨论】:

    标签: c++ templates sfinae typetraits enable-if


    【解决方案1】:

    T 在您的类成员函数被实例化时已为人所知,因此不会发生替换,而且您会得到一个硬错误,而不是 SFINAE。最简单的解决方法是为这些运算符重载引入一个虚拟模板参数,并将其默认为T,这样仍然可以进行类型推导。

    template<typename U = T>
    operator typename std::enable_if<!std::is_class<U>::value, U >::type () const
    {
        return _val;
    }
    
    template<typename U = T>
    operator typename std::enable_if< std::is_class<U>::value, const U&>::type () const
    {
        return _val;
    }
    

    Live demo

    【讨论】:

    • 对于稍微不同的示例和其他详细信息,请参阅stackoverflow.com/questions/18100297/…
    • @Asher 在默认模板参数中使用 SFINAE 在您链接到的问题中有效,但在上面的问题中,OP 试图使用它来定义两个互斥重载,它赢了't as explained here.
    • 这可能不允许编译器也实例化不需要的类型转换运算符吗?
    • @Museful 你说的不需要是什么意思?能举个例子吗?
    • 也许我更应该问这个问题:使用T 而不是U 作为enable_if 的第二个参数是否完全等效? (和const T&amp; 而不是const U&amp;
    【解决方案2】:

    虽然没有解决为什么不丢弃不正确的运算符的问题,但要解决手头的特定问题,即对于类类型按 const ref 返回或对于其他类型按值返回,一个可以使用std::conditional找到解决方案。

    template< bool B, class T, class F >
    struct conditional;
    

    提供成员 typedef 类型,如果 B 为真,则定义为 T 编译时间,如果 B 为假,则为 F。

    工作示例:

    #include <iostream>
    #include <type_traits>
    
    template<typename T>
    struct Foo
    {
        operator typename std::conditional<
            std::is_class<T>::value, const T&, T>::type () const
        {
            return _val;
        }
    
        T _val;
    };
    
    int main()
    {
        Foo<double> f1;
        f1._val = 0.3;
    
        double d = f1;
        std::cout << d << std::endl;
        return 0;
    }
    

    【讨论】:

    • 这似乎是避免需要回答您的问题的一种不错的方法,但值得注意的是,当您的操作员的身体不是 100% 相同时,它不能推广到工作。
    • @hvd 我同意。然而,在这个特定的例子中,我想要做的就是通过 const ref 返回类类型,并通过值返回其他类型,我认为使用 std::conditional 比使用 2 个 std_enable_if 重载更简单/更短.你同意吗?
    • 是的,如果两个运算符的主体相同,我肯定会使用std::conditional 解决方案。
    • @SteveLorimer 当然,我同意这也是一种改进。
    • 由于某种原因,我在尝试通过隐式转换(即3 + Foo / 2)使用算术时遇到了 std::enable_if 重载版本的问题。不过,std::conditional 版本完美运行,新的 if constexpr 构造现在应该允许不同的运算符主体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 2017-12-27
    • 2021-09-26
    相关资源
    最近更新 更多