【问题标题】:Specialize cast operator based on type traits基于类型特征的特殊转换运算符
【发布时间】:2017-10-15 03:20:28
【问题描述】:

这是我之前的question 的后续。

我有一个对任何东西都有强制转换运算符的类。在 C++17 之前的环境中,这会产生在执行初始化时无法选择适当的构造函数重载的错误。我想通过为某些类型标记强制转换运算符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&lt;typename U, typename = void&gt; operator U(); 类型的函数并对其进行部分特化,因为不允许部分函数特化,而且创建辅助类对我来说似乎有点过头了。

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

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

【问题讨论】:

    标签: c++ casting template-meta-programming sfinae


    【解决方案1】:

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

    #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

    【讨论】:

    • 这很有趣。
    【解决方案2】:

    您可以使用非类型模板参数来避免“不能重载”的问题:

    #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

    【讨论】:

      【解决方案3】:

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

      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;
          }
      };
      

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

      【讨论】:

        【解决方案4】:

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

        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 启用对整数类型的显式强制转换。

        【讨论】:

        • 希望你不介意我冒昧地改进了格式(没有水平滚动),并删除了 enable_if 的无用默认参数
        • 一点也不。我很感激。谢谢!
        • 添加类型名 std::enable_if::value>::type* = nullptr 作为显式运算符重载的第二个模板参数将使程序支持“不必要" 显式转换 s.t. int z = static_cast(c);现在,这将是一个模棱两可的调用,但是应该始终允许明确地写出强制转换。
        • @Jodocus 我同意应该支持这些。我回家后会查看它,但如果您确定它可以编译并且可以正常工作,请随时进行修复。感谢您的意见。
        猜你喜欢
        • 2020-06-06
        • 1970-01-01
        • 1970-01-01
        • 2019-01-30
        • 2021-10-23
        • 1970-01-01
        • 2022-07-10
        • 2022-01-11
        • 2019-04-21
        相关资源
        最近更新 更多