1

想尝试一下,我最近决定尝试在 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;
};

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

所以,我的问题是:operator+=(T)除了优化临时复合体以及template <typename U>如果所有其他复合体类型都隐式转换为复合体,为什么还要使用嵌套?

4

2 回答 2

1

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

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

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

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

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

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

虽然这对于基本类型并不重要,但如果用于实例化模板的类型是用户定义的并且构造/复制成本很高,则可能是这样。例如,考虑一种BigNum动态分配内存的类型。临时会导致内存分配和释放。

[*] 您不允许默认构造或带有单个参数的构造。

于 2013-10-28T17:53:11.557 回答
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

还有另一个区别。如果我们有一个带有 的类Baroperator Complex<double>() const它将无法与 C++ 标准版本一起使用。

使用您的版本,它的工作原理Tdouble.

简而言之,您的参数可隐式转换为特定类型/从特定类型转换与采用该类型的参数不同。只能尝试一种用户定义的转换,因此如果您采用int可以转换为的类型int是可接受的,并且如果您采用的是可以从 制作的类型,则int并非所有可以转换为的类型int都是可接受的。

非用户定义的转换不受相同方式的限制。

于 2013-10-28T17:57:38.227 回答