模板化的复制构造函数失败,具有特定的模板化类型

时间:2009-08-08 20:29:11

标签: c++ constructor type-conversion copy-constructor

由于我的一些代码需要在不同类型的矩阵之间进行隐式转换(例如Matrix<int>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错误而失败: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));
        }
    }
}

2 个答案:

答案 0 :(得分:13)

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

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

  

此外,为什么需要非常详细的template <typename T> template <typename U>语法

因为涉及两个模板:Matrix,它是一个类模板,以及转换构造函数模板。每个模板都有自己的模板子句及其自己的参数。

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

  

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

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


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

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

复制构造函数也可以具有非const引用参数,如果其任何成员具有

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

答案 1 :(得分:1)

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