8

由于我的一些代码需要在不同类型的矩阵之间进行隐式转换(例如Matrix<int>to Matrix<double>),我定义了一个模板化的复制构造函数Matrix<T>::Matrix(Matrix<U> const&)而不是标准的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 错误而失败: where U == T。果然,使用默认Matrix<T>::Matrix(Matrix<T> const&)签名重载复制构造函数可以解决问题。

这是一个糟糕的解决方案,因为它会导致复制构造器代码的大量复制(字面意思是未更改的复制和粘贴)。更重要的是,我不明白为什么malloc没有重复代码会出现双释放错误。此外,为什么这里需要非常冗长的template <typename T> template <typename U>语法而不是标准的语法,而且更简洁,template <typename T, typename U>

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

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

2 回答 2

13

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

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

此外,为什么template <typename T> template <typename U>需要极其冗长的语法

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

顺便说一句,您应该摆脱<T>第一行中的 。定义模板时不会出现这样的事情。

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

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


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

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

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

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)
}
于 2009-08-08T20:42:34.093 回答
1

我对您的问题并不完全清楚,但我怀疑正在发生的事情是默认复制构造函数(仅执行成员复制)正在您的代码中的某些地方使用。请记住,不仅您实际编写的代码使用复制构造函数——编译器也使用它。

于 2009-08-08T20:47:36.253 回答