2009-08-08 4 views
8

Как некоторые из моего кода требуется неявное преобразование между матрицами различных типов (например, Matrix<int> к Matrix<double>), я определил шаблонный конструктор копирования Matrix<T>::Matrix(Matrix<U> const&) вместо стандартного Matrix<T>::Matrix(Matrix<T> const&):Templated копирование конструктор не может с определенным шаблонным типом

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

Полный источник шаблонного метода, скомпилированный с использованием G ++ v4.0.1 на 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)); 
     } 
    } 
} 

ответ

13

Не удается, так как шаблон не подавляет неявное объявление конструктора копии. Он будет служить в качестве простого конструктора преобразования, который может использоваться для копирования объекта, когда его выбирает разрешение перегрузки.

Теперь вы, вероятно, скопировали свою матрицу где-нибудь, в которой будет использоваться неявно определенный конструктор копирования, который выполняет плоскую копию. Затем скопированная матрица и копия будут как в своем деструкторе удалять один и тот же указатель.

Кроме того, почему крайне многословный template <typename T> template <typename U> синтаксиса требуется

Поскольку есть два шаблона, связанные: Матрица, которая представляет собой шаблон класса, и преобразовывая шаблон конструктора. Каждый шаблон заслуживает собственного предложения шаблона со своими собственными параметрами.

Вы должны избавиться от <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) 
} 
+0

Ваши объяснения звуков ошибки malloc появляются. Существует ли конкретная причина, почему компиляторы не могут использовать функцию-член шаблона в качестве конструктора-копии? Спасибо за информацию и за советы, чтобы избежать дублирования кода. –

+0

Если у вас есть этот шаблон, это еще не функция. Только его использование с некоторым аргументом шаблона генерирует из него (член) функцию (называемую специализацией). Это также причина, по которой шаблоны членов не могут быть виртуальными: вы заранее не знаете, какие функции генерируются из него. –

+0

Это имеет большой смысл - это не работает по той причине, что нужно либо переслать объявить параметры шаблона, либо включить полный источник реализации. Еще раз спасибо. –

1

Я не совсем ясно из вашего вопроса, но Я подозреваю, что происходит то, что конструктор копии по умолчанию (который делает только копию по порядку) используется в некоторых местах вашего кода. Помните, что не только код, который вы на самом деле пишете, использует конструктор копирования - его тоже использует компилятор.

Смежные вопросы