模板复制构造函数因特定模板类型而失败

由于我的一些代码需要在不同类型的矩阵之间进行隐式转换(例如 Matrix<int>Matrix<double>),我定义了一个模板化的复制构造函数 Matrix<T>::Matrix(Matrix<U> const&) 代替标准的 Matrix<T>::Matrix(Matrix<T> const&)::p>

As some of my code required implicit conversion between matrices of different types (e.g. Matrix<int> to Matrix<double>), I defined a templated copy constructor Matrix<T>::Matrix(Matrix<U> const&) instead of the standard Matrix<T>::Matrix(Matrix<T> const&):

template <typename T> class Matrix {
public:
    // ...
    template <typename U> Matrix(Matrix<U> const&);
    // ...
private
    unsigned int m_rows, m_cols;
    T *m_data;
    // ...
};

在复制构造函数中添加适当的类型转换后,此方法可以在不同类型的矩阵之间完美转换.令人惊讶的是,在简单的复制构造函数可以运行的情况下,它会因 malloc 错误而失败:U == T.果然,用默认的 Matrix<T>::Matrix(Matrix<T> const&) 签名重载复制构造函数可以解决问题.

With an appropriate typecast added to the copy-constructor, this method flawlessly converted between matrices of different types. Surprisingly, it fails with a malloc error in the very situation where a simple copy-constructor would function: where U == T. Sure enough, overloading the copy-constructor with the default Matrix<T>::Matrix(Matrix<T> const&) signature solves the problem.

这是一个糟糕的解决方案,因为它会导致大量复制构造函数代码(字面意思是未更改的复制和粘贴).更重要的是,我不明白为什么在没有重复代码的情况下会出现 double-free malloc 错误.此外,为什么非常冗长的 template <typename T>这里需要的模板 语法与标准相反,并且更简洁,template ?

This is a poor solution, as it results in the wholesale duplication of the copy-constructor code (Literally an unchanged copy-and-paste). More importantly, I do not understand why there is a double-free malloc error without the duplicate code. Furthermore, why is the extremely verbose template <typename T> template <typename U> syntax required here as opposed to the standard, and much more succinct, template <typename T, typename U>?

模板化方法的完整源代码,在 Mac OS 10.5 上使用 G++ v4.0.1 编译.

Full source of the templated method, compiled using G++ v4.0.1 on Mac OS 10.5.

template <typename T> template <typename U> Matrix<T>::Matrix(Matrix<U> const& obj) {
    m_rows = obj.GetNumRows();
    m_cols = obj.GetNumCols();
    m_data = new T[m_rows * m_cols];

    for (unsigned int r = 0; r < m_rows; ++r) {
        for (unsigned int c = 0; c < m_cols; ++c) {
            m_data[m_rows * r + c] = static_cast<T>(obj(r, c));
        }
    }
}

推荐答案

失败是因为模板没有抑制复制构造函数的隐式声明.它将作为一个简单的转换构造函数,可用于在重载决议选择对象时复制对象.

It fails because a template doesn't suppress the implicit declaration of a copy constructor. It will serve as a simple converting constructor, which can be used to copy an object when overload resolution selects it.

现在,您可能在某处复制了矩阵,这将使用隐式定义的复制构造函数来执行平面复制.然后,复制的矩阵和副本都将在它们的析构函数中删除相同的指针.

Now, you probably copied your matrix somewhere, which would use the implicitly defined copy constructor which does a flat copy. Then, the copied matrix and the copy would both in their destructor delete the same pointer.

此外,为什么非常冗长的 template <typename T>模板<typename U>语法要求

Furthermore, why is the extremely verbose template <typename T> template <typename U> syntax required

因为涉及到两个模板:Matrix(类模板)和转换构造函数模板.每个模板都应该有自己的模板子句和自己的参数.

Because there are two templates involved: The Matrix, which is a class template, and the converting constructor template. Each template deserves its own template clause with its own parameters.

顺便说一句,你应该去掉第一行中的 <T> .定义模板时不会出现这样的事情.

You should get rid of the <T> in your first line, by the way. Such a thing does not appear when defining a template.

这是一个糟糕的解决方案,因为它会导致大量复制构造函数代码

This is a poor solution, as it results in the wholesale duplication of the copy-constructor code

您可以定义一个成员函数模板来完成工作,并从转换构造函数和复制构造函数进行委托.这样代码就不会重复了.

You can define a member function template, which will do the work, and delegate from both the converting constructor and the copy constructor. That way, the code is not duplicated.

理查德在评论中提出了一个很好的观点,这让我修改了我的答案.如果从模板生成的候选函数比隐式声明的复制构造函数更匹配,那么模板获胜",它将被调用.下面是两个常见的例子:

Richard made a good point in the comments which made me amend my answer. If the candidate function generated from the template is a better match than the implicitly declared copy constructor, then the template "wins", and it will be called. Here are two common examples:

struct A {
  template<typename T>
  A(T&) { std::cout << "A(T&)"; }
  A() { }
};

int main() {
  A a;
  A b(a); // template wins:
          //   A<A>(A&)  -- specialization
          //   A(A const&); -- implicit copy constructor
          // (prefer less qualification)

  A const a1;
  A b1(a1); // implicit copy constructor wins: 
            //   A(A const&) -- specialization
            //   A(A const&) -- implicit copy constructor
            // (prefer non-template)
}

复制构造函数也可以有一个非常量引用参数,如果它的任何成员有

A copy constructor can have a non-const reference parameter too, if any of its members has

struct B { B(B&) { } B() { } };
struct A {
  template<typename T>
  A(T&) { std::cout << "A(T&)"; }
  A() { }
  B b;
};

int main() {
  A a;
  A b(a); // implicit copy constructor wins:
          //   A<A>(A&)  -- specialization
          //   A(A&); -- implicit copy constructor
          // (prefer non-template)

  A const a1;
  A b1(a1); // template wins: 
            //   A(A const&) -- specialization
            // (implicit copy constructor not viable)
}

相关文章