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

发布于 2024-07-30 20:04:08 字数 1512 浏览 5 评论 0原文

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

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::Matrix(Matrixconst&) 签名重载复制构造函数可以解决问题。

这是一个糟糕的解决方案,因为它会导致复制构造函数代码的大量重复(实际上是未更改的复制和粘贴)。 更重要的是,我不明白为什么在没有重复代码的情况下会出现双重释放 malloc 错误。 此外,为什么极其冗长的 template是这样的? 这里需要 template 语法,与标准相反,并且更简洁,template

模板化方法的完整源代码,在 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));
        }
    }
}

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;
    // ...
};

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.

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>?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

简美 2024-08-06 20:04:09

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

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

此外,为什么极其冗长的 template是这样的? 模板 需要语法

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

顺便说一句,您应该去掉第一行中的 。 定义模板时不会出现这种情况。

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

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


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

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

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.

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

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.

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)
}
少钕鈤記 2024-08-06 20:04:09

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

I'm not entirely clear from your question, but I suspect what is happening is that the default copy constructor (which does a memberwise copy only) is being used in some places in your code. Remember, not only the code you actually write uses the copy constructor - the compiler uses it too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文