Em uma classe sem herança, esperaria que a virtual
palavra-chave não tivesse nenhum efeito perceptível. No entanto, no exemplo de código a seguir, adicioná-lo interrompe a compilação. Eu gostaria de entender o mecanismo por trás disso. Por que a virtual
palavra-chave interrompe a compilação?
Compila bem sem a palavra-chave virtual.
#define USE_VIRTUAL 1
// compile with: cl /W4 /EHsc main.cpp /link /out:main.exe
#include<iostream>
#include<memory>
struct Dummy {int i = 3;};
class Uncopyable {
public:
// Implicitly delete the copy constructor by having
// a non-copyable member.
std::unique_ptr<Dummy> m_innerPtr;
// A virtual destructor to allow correct inheritance.
// I first thought that this implicitly deletes the
// copy-assignment and copy-constructor,
// but it does not. It does, however, implicitly
// delete the move constructor and assignment.
virtual ~Uncopyable() = default;
// accessible constructor
Uncopyable() = default;
Uncopyable(std::unique_ptr<Dummy> dummy):m_innerPtr(std::move(dummy)){};
};
template <typename MaybeCopyable>
class Bamboozle {
public:
void foo(std::shared_ptr<MaybeCopyable> obj) {
std::cout << "Reached Bamboozle::foo(shared_ptr)" << std::endl;
}
// overload that takes an object by value
#if USE_VIRTUAL
virtual
#endif
void foo(MaybeCopyable obj){
std::cout << "Reached Bamboozle::foo(Uncopyable)" << std::endl;
foo(std::make_shared<Uncopyable>(std::move(obj)));
}
};
int main(int argc, char** argv){
// construct param objects
std::shared_ptr<Uncopyable> uncSharedPtr = std::make_shared<Uncopyable>();
// prints Bamboozle::foo(shared_ptr)
Bamboozle<Uncopyable> bamboozleObj;
bamboozleObj.foo(uncSharedPtr);
return 0;
};
Veja esta questão (fechada por ser muito detalhada) para obter mais contexto se quiser ler sobre outros aspectos que acho que estão envolvidos nisso. Especificamente modelos e construtores de movimentos. O modelo de classe é necessário para reproduzir esse comportamento.
A mensagem de erro é:
cl /W4 /EHsc main.cpp /link /out:main.exe
Microsoft (R) C/C++ Optimizing Compiler Version 19.38.33134 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
main.cpp(46): warning C4100: 'argv': unreferenced formal parameter
main.cpp(46): warning C4100: 'argc': unreferenced formal parameter
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\xutility(255): error C2280: 'Uncopyable::Uncopyable(const Uncopyable &)': attempting to reference a deleted function
main.cpp(24): note: compiler has generated 'Uncopyable::Uncopyable' here
main.cpp(24): note: 'Uncopyable::Uncopyable(const Uncopyable &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<Dummy,std::default_delete<Dummy>>::unique_ptr(const std::unique_ptr<Dummy,std::default_delete<Dummy>> &)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\memory(3319): note: 'std::unique_ptr<Dummy,std::default_delete<Dummy>>::unique_ptr(const std::unique_ptr<Dummy,std::default_delete<Dummy>> &)': function was explicitly deleted
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\xutility(255): note: the template instantiation context (the oldest one first) is
main.cpp(51): note: see reference to class template instantiation 'Bamboozle<Uncopyable>' being compiled
main.cpp(38): note: while compiling class template member function 'void Bamboozle<Uncopyable>::foo(MaybeCopyable)'
with
[
MaybeCopyable=Uncopyable
]
main.cpp(40): note: see reference to function template instantiation 'std::shared_ptr<Uncopyable> std::make_shared<Uncopyable,Uncopyable>(Uncopyable &&)' being compiled
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\memory(2769): note: see reference to function template instantiation 'std::_Ref_count_obj2<_Ty>::_Ref_count_obj2<_Ty>(_Ty &&)' being compiled
with
[
_Ty=Uncopyable
]
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\memory(2094): note: see reference to function template instantiation 'void std::_Construct_in_place<_Ty,_Ty>(_Ty &,_Ty &&) noexcept(false)' being compiled
with
[
_Ty=Uncopyable
]
Adicionar um destruidor remove silenciosamente o construtor de movimento e a atribuição de movimento. Ele deve remover o construtor de cópia e a atribuição de cópia também, mas atualmente não o faz (isso está obsoleto), portanto, mover essa classe a copia silenciosamente.
Sua classe não possui operações de cópia para as quais recorrer; portanto, quando ela perde as operações de movimentação, ela se torna imóvel.
Adicione isso:
Quanto ao porquê de adicionar
virtual
quebra as coisas.virtual
força a função a ser instanciada incondicionalmente, enquanto as funções não virtuais só são instanciadas se usadas e você não pode chamar a função ofensiva.