Templated copy-construtor falha com específica templated tipo
-
12-09-2019 - |
Pergunta
Como alguns dos meu código necessário conversão implícita entre matrizes de tipos diferentes (por exemplo Matrix<int>
para Matrix<double>
), que definida uma cópia construtor Matrix<T>::Matrix(Matrix<U> const&)
modelada em vez do Matrix<T>::Matrix(Matrix<T> const&)
padrão:
template <typename T> class Matrix {
public:
// ...
template <typename U> Matrix(Matrix<U> const&);
// ...
private
unsigned int m_rows, m_cols;
T *m_data;
// ...
};
Com um estereotipado adequado adicionado à cópia-construtor, este método perfeitamente convertidos entre as matrizes de diferentes tipos. Surpreendentemente, ele falha com um erro de malloc na mesma situação em que uma cópia construtor simples funcionaria: onde U == T
. o suficiente certeza, sobrecarregando o copy-construtor com os resolve assinatura Matrix<T>::Matrix(Matrix<T> const&)
padrão o problema.
Esta é uma má solução, uma vez que resulta na duplicação atacado do código copy-construtor (Literalmente uma cópia e colar inalterado). Mais importante, eu não entendo por que há um erro de double-free malloc
sem o código duplicado. Além disso, por que é extremamente detalhado sintaxe template <typename T> template <typename U>
necessária aqui, em oposição ao padrão, e muito mais sucinta, template <typename T, typename U>
?
-fonte completo do método templated, compilado usando G ++ v4.0.1 no 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));
}
}
}
Solução
Ele falha porque um modelo não suprime a declaração implícita de um construtor de cópia. Ele irá servir como uma simples conversão de construtor, que pode ser usado para copiar um objecto quando selecciona a resolução de sobrecarga-lo.
Agora, você provavelmente copiou o seu em algum lugar matriz, que usaria o construtor de cópia definido implicitamente que faz uma cópia simples. Em seguida, a matriz copiado e a cópia seria tanto em seu destruidor excluir o mesmo ponteiro.
Além disso, por que é extremamente detalhado sintaxe
template <typename T> template <typename U>
necessário
Porque existem dois modelos envolvidos: The Matrix, que é um modelo de classe, eo modelo de construtor de conversão. Cada modelo merece sua própria cláusula modelo com seus próprios parâmetros.
Você deve se livrar do <T>
em sua primeira linha, pela maneira. coisa tal não aparece quando definir um modelo.
Esta é uma má solução, uma vez que resulta na duplicação atacado do código cópia construtor
Você pode definir um modelo de função de membro, que vai fazer o trabalho, e delegar tanto o construtor conversão e o construtor de cópia. Dessa forma, o código não é duplicada.
Richard fez um bom ponto nos comentários que me fez alterar a minha resposta. Se a função de candidato gerado a partir do modelo é um jogo melhor do que o construtor de cópia declarou implicitamente, em seguida, os modelo "ganha", e ele será chamado. Aqui estão dois exemplos comuns:
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)
}
Um construtor de cópia pode ter um parâmetro de referência não-const também, se qualquer de seus membros tem
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)
}
Outras dicas
Eu não sou inteiramente claro da sua pergunta, mas eu suspeito que está acontecendo é que o construtor de cópia padrão (que faz um memberwise cópia somente) está sendo usado em alguns lugares em seu código. Lembre-se, não só o código que você usa realmente escrever o construtor de cópia -. Os usos do compilador também