根据类型特征专门化强制转换运算符

Specialize cast operator based on type traits

这是我之前 .

的后续

我有一个 class 和任何东西的强制转换运算符。在 C++17 之前的环境中,这会产生在执行初始化时无法 select 适当的构造函数重载的错误。我想通过为某些类型标记转换运算符 explicit 来调整行为。但是,我找不到这样做的方法。

这是一个人为的例子:我想要一个隐式转换运算符到整数类型,并显式转换为所有其他类型。

这不起作用,因为我们无法确定 U 具有类型 typename std::enable_if<!std::is_integral<U>::value, U>::type:

的表达式
struct C {
    template<typename U>
    operator typename std::enable_if< std::is_integral<U>::value, U>::type() const {
        return 1;
    }

    template<typename U>
    explicit operator typename std::enable_if<!std::is_integral<U>::value, U>::type() const {
        return 1.5;
    }
};

这个编译失败说 C::operator U() cannot be overloaded:

struct C {
    template<typename U, typename = typename std::enable_if< std::is_integral<U>::value, U>::type>
    operator U() const {
        return 1;
    }

    template<typename U, typename = typename std::enable_if<!std::is_integral<U>::value, U>::type>
    explicit operator U() const {
        return 1.5;
    }
};

我无法声明类型 template<typename U, typename = void> operator U(); 的函数并对其进行部分特化,因为不允许进行部分函数特化,并且制作一个助手 class 对我来说似乎有点过分了。

如何根据要转换为的类型的某些特征声明转换运算符?

我需要一个 C++11 解决方案,因为在 C++17 中,我上一个问题的问题已经 .b

试试这个。只需取消对显式运算符的约束,因为它涵盖了第一个运算符没有涵盖的所有情况。

Coliru 示例:http://coliru.stacked-crooked.com/a/3d0bc6e59ece55cf

#include <iostream>
#include <type_traits>

struct C {
    template <typename U,
              typename = typename std::enable_if< std::is_integral<U>::value>::type>
    operator U() const {
        return 1;
    }

    template<typename U, typename std::enable_if<!std::is_integral<U>::value>::type* = nullptr>
    explicit operator U() const {
        return 1.5;
    }
};

int main() {
    C c;
    int v = c;
    int w = c;
    int x = static_cast<int>(c);
    long y = static_cast<int>(c);
    double z = static_cast<double>(c);

    std::cout << v << std::endl;
    std::cout << w << std::endl;
    std::cout << x << std::endl;
    std::cout << y << std::endl;
    std::cout << z << std::endl;
}

感谢@Jodocus 启用对整数类型的显式转换。

您可以将这些运算符的定义移动到基础 类。这种方法允许您对隐式和显式运算符施加约束:

#include <type_traits>
#include <iostream>

template<typename TDerived> class
t_ImplicitlyConvertableToAnything
{
    public: template
    <
        typename TTarget
    ,   typename TEnabled = typename ::std::enable_if_t<::std::is_integral<TTarget>::value>
    >
    operator TTarget(void) const
    {
        auto const & self{static_cast<const TDerived &>(*this)};
        return(self.template CheckedConversion_To_Integral<TTarget>());
    }
};

template<typename TDerived> class
t_ExplicitlyConvertableToAnything
{
    public: template
    <
        typename TTarget
    ,   typename TEnabled = typename ::std::enable_if_t<!::std::is_integral<TTarget>::value>
    > explicit
    operator TTarget(void) const
    {
        auto const & self{static_cast<const TDerived &>(*this)};
        return(self.template CheckedConversion_To_NonIntegral<TTarget>());
    }
};

class
t_ConvertableToAnything
:   public t_ImplicitlyConvertableToAnything<t_ConvertableToAnything>
,   public t_ExplicitlyConvertableToAnything<t_ConvertableToAnything>
{
    public: template<typename TTarget> decltype(auto)
    CheckedConversion_To_Integral(void) const
    {
        return(static_cast<TTarget>(1));
    }

    public: template<typename TTarget> decltype(auto)
    CheckedConversion_To_NonIntegral(void) const
    {
        return(static_cast<TTarget>(3.14));
    }
};


int main()
{
    t_ConvertableToAnything c;
    ::std::cout << ([](int x){return(x);})(c) << ::std::endl;
    ::std::cout << static_cast<float>(c) << ::std::endl;
    return(0);
}

Run this code online

您可以使用非类型模板参数来避免 "cannot be overloaded" 问题:

#include <iostream>
#include <type_traits>

struct A { };
struct B { };

struct C {
    template <typename U,
              typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
    explicit operator U() const {
        return 1;
    }

    template<typename U,
     typename std::enable_if<std::is_same<U, A>::value>::type* = nullptr>
    explicit operator U() const {
        return A{ };
    }

     template<typename U,
     typename std::enable_if<std::is_same<U, B>::value>::type* = nullptr>
    explicit operator U() const {
        return B{ };
    }

};

int main() {
    C c;
    long y = static_cast<int>(c);
    B b = static_cast<B>(c);
    A a = static_cast<A>(c);
}

https://ideone.com/smfPwF

您可以使用带有虚拟模板参数的技巧来重载您的转换运算符以消除歧义。

struct C {
    template<typename U, 
             typename = typename enable_if<is_integral<U>::value, U>::type, 
             int = 0> // <== hete
    operator U() const {
        return 1;
    }

    template<typename U, 
             typename = typename enable_if<!is_integral<U>::value, U>::type, 
             char = 0> //  <== and here
    explicit operator U() const {
        return 1.5;
    }
};

由于现在模板签名不同,所以没有歧义。