C++ usando copy-ctor quando operator=() é usado - exatamente como isso funciona?
-
21-12-2019 - |
Pergunta
Quais são exatamente as regras para C++ converter uma atribuição de operador=() em uma construção?Como Foo foo = bar
na verdade, chamará o construtor de Foo aceitando bar como argumento, se existir.Pesquisei no Google como isso funciona, mas não consigo encontrar nada.
Estou tendo problemas para descobrir por que a tarefa abaixo está tentando usar um construtor, mas não está usando o obviamente correto:HandlePtr( TYPE& recurso ).A construção usando a sintaxe de construção real funciona bem, mas não com o operador de atribuição.
código (obviamente editado por questões de brevidade):
template< typename TYPE >
class HandlePtr {
public:
HandlePtr( void ) = default;
HandlePtr( HandlePtr< TYPE >& other ) = default;
HandlePtr( TYPE& resource ) {} // generally I would make this explicit, but for testing purposes I took it out
~HandlePtr( void ) = default;
public:
HandlePtr<TYPE>& operator=( TYPE& resource ) { return *this; }
HandlePtr<TYPE>& operator=( HandlePtr<TYPE>& other ) { return *this; }
};
int main ( void ) {
int x = 5;
HandlePtr< int > g( x ); // works
HandlePtr< int > i;i = x; // works
HandlePtr< int > h = x; // doesn't work
// also tried this just out of curiosity:
HandlePtr< int > h = HandlePtr< int >( x ); // also does not work
return 0;
}
erros:
shit.cpp: In function ‘int main()’:
try.cpp:19:24: error: no matching function for call to ‘HandlePtr<int>::HandlePtr(HandlePtr<int>)’
HandlePtr< int > h = x; // doesn't work
^
try.cpp:19:24: note: candidates are:
try.cpp:7:3: note: HandlePtr<TYPE>::HandlePtr(TYPE&) [with TYPE = int]
HandlePtr( TYPE& resource ) {} // generally I would make this explicit, but for testing purposes I took it out
^
try.cpp:7:3: note: no known conversion for argument 1 from ‘HandlePtr<int>’ to ‘int&’
try.cpp:6:3: note: HandlePtr<TYPE>::HandlePtr(HandlePtr<TYPE>&) [with TYPE = int]
HandlePtr( HandlePtr< TYPE >& other ) = default;
^
try.cpp:6:3: note: no known conversion for argument 1 from ‘HandlePtr<int>’ to ‘HandlePtr<int>&’
try.cpp:5:3: note: HandlePtr<TYPE>::HandlePtr() [with TYPE = int]
HandlePtr( void ) = default;
^
try.cpp:5:3: note: candidate expects 0 arguments, 1 provided
try.cpp:20:20: error: redeclaration of ‘HandlePtr<int> h’
HandlePtr< int > h = HandlePtr< int >( x ); // also does not work
^
try.cpp:19:20: error: ‘HandlePtr<int> h’ previously declared here
HandlePtr< int > h = x; // doesn't work
Solução
Você está negligenciando isso no declaração:
T t = u;
este não é o operador de atribuição. t = u;
não é uma subexpressão de uma declaração.A única expressão aqui é u
;e o resultado da avaliação da expressão u
é usado como inicializador para o objeto t
sendo declarado.
Se u
tem tipo T
, então t
é construído por cópia a partir de u
.
Se u
não tem tipo T
, então u
primeiro precisa ser convertido para o tipo T
.Isto cria um valor do tipo T
.
Você não tem nenhum construtor que aceite um rvalue, então T t = u;
, e o idêntico T t = T(u);
ambos falham.No entanto, T t(u)
é bem-sucedido porque nenhum valor é criado;O valor que u
é usado como argumento para o construtor T(U &)
.
Exemplo de código simplificado:
struct T
{
T(int &);
T(T&);
T();
T &operator=(int &);
};
int main()
{
int x = 5;
T g(x); // OK, T(int &)
T g2(5); // fail, looks for T(int const &)
T i; // OK, T()
i = x; // OK, T::operator=(int&)
T h3 = i; // OK, T(T&)
T h1 = T(x); // fail, looks for T(T const &)
T h2 = x; // fail, identical to previous line
}
Normalmente você deve usar const &
como parâmetro para construtores de cópia e operadores de atribuição;então, todos esses casos de "falha" tornam-se "OK" , pois um rvalue pode ser vinculado a uma referência const.