模板重载解析和隐式转换

时间:2013-10-28 17:15:05

标签: c++ templates overloading implicit-conversion

我想尝试一下,最近我决定尝试在C ++中实现一个模板化复数类。作为参考,我使用了C ++ 11标准库实现,并且我遇到了该实现与我的实现之间的区别。它指的是他们如何在类中重载+ =运算符。

在我的基础上我基本上只有一个+ =方法,它能够同时处理Complex<T> += Complex<U>Complex<T> += whatever other type that implicitly converts to T and thus Complex<T>。为了澄清这里是类声明:

template <typename T>
class Complex {
    public:
        constexpr Complex (T=T(), T=T()); // added for clarity

        template <typename U>
        constexpr Complex (const Complex <U>&);
        template <typename U>
        constexpr Complex (Complex <U>&&);
        template <typename U>
        Complex <T>& operator= (const Complex <U>&);
        template <typename U>
        Complex <T>& operator= (Complex <U>&&);

        Complex& operator+= (const Complex<T>&);
        Complex& operator-= (const Complex<T>&);
        Complex& operator*= (const Complex<T>&); 
        Complex& operator/= (const Complex<T>&); 
        Complex& operator++ (void);
        Complex operator++ (int);
        Complex& operator-- (void);
        Complex operator-- (int);

        std::string to_string (void) const;

        constexpr T real (void);
        constexpr T img (void);
        void real (const T&);
        void img (const T&);

    private:
        T _m_real, _m_img;
        template <typename U>
        friend class Complex;
};

然而,在标准库实现中,他们使用operator+=的2个重载,一个采用Complex<U>,另一个采用T。就我测试而言,这两种实现似乎都会产生相同的行为:在任何两个复杂类型之间添加,或者复杂和另一种类型隐式转换为复杂内部的类型。

所以,我的问题是:除了优化临时复合体之外,还有任何理由可以单独使用operator+=(T)吗?如果所有其他复杂类型隐式转换为Complex,为什么要使用nestde template <typename U>? p>

2 个答案:

答案 0 :(得分:1)

您要比较的两个接口实际上是:

// 1
Complex<T>& operator+=(const Complex<T>&);

// 2
template <typename X>
complex<T>& operator+=(const complex<X>&);
complex<T>& operator+=(const T&);

两个不同之处在于,在2中,带有complex的重载是参数类型的模板,并且存在直接获取T的显式重载。由于存在(假设您添加了缺少的构造函数[*])转换,两种方法基本上都允许编译相同的代码,但需要额外的费用。

如果你只想在复数的实部添加一个值,那么第一个设计需要创建一个Complex<T>(假设你有那个构造函数,没有显示),而在第二种情况不需要创建临时对象。

类似地,作为参数给出complex类型的不同实例化,隐式转换将在1中用于创建随后将被添加的临时转换,而在第二种方法中,参数将直接绑定而不用需要一个临时的。

虽然这对于基本类型并不重要,但是如果用于实例化模板的类型是用户定义的并且构造/复制是昂贵的。例如,考虑动态分配内存的BigNum类型。临时将导致内存分配和释放。

[*]您不允许使用单个参数进行默认构造或构造。

答案 1 :(得分:1)

struct Foo {
  operator int()const {
    return 7;
  }
};`

由于只会调用一个用户定义的转化,因此Foo会增加问题。

举个例子:

Complex<double> d;
Foo f;
d += f; // fails to compile with your version, compiles with C++ standard one

还有另一个区别。如果我们的类Bar具有operator Complex<double>() const,则它将无法使用C ++标准版本。

对于您的版本,iff T正好是double

简而言之,您的参数可以隐式转换为特定类型或从特定类型转换为与该类型的参数不同。只能尝试一次用户定义的转换,因此,如果您接受可转换为int的{​​{1}}类型,则可以接受,如果您选择的类型可以来自int,则不是全部可以转换为int的类型是可以接受的。

非用户定义的转化不受同样限制。