Eu sei que variáveis/objetos têm ligação interna e externa, mas não sabia que "tipos" têm ligação interna e externa. Quando compilo meu código com o GCC, recebo o aviso:
'ShaderModuleObject' tem um campo 'GFXAPIShaderModuleHandle ShaderModuleObject::handle' cujo tipo tem ligação interna [-Wsubobject-linkage]
Aqui está o código, e eu o colei no Godbolt , mas não vejo o mesmo aviso, ou mesmo se o Godbolt tem avisos habilitados:
#define GFX_API_NULL_HANDLE 0
template <typename T, void(*deleter)(T)>
struct MoveOnlyGFXAPIHandle
{
MoveOnlyGFXAPIHandle() : handle(GFX_API_NULL_HANDLE) {}
MoveOnlyGFXAPIHandle(T handle) : handle(handle) {}
MoveOnlyGFXAPIHandle(const MoveOnlyGFXAPIHandle&) = delete;
MoveOnlyGFXAPIHandle(MoveOnlyGFXAPIHandle&& other) : handle(other.handle) { other.handle = GFX_API_NULL_HANDLE; };
MoveOnlyGFXAPIHandle& operator=(const MoveOnlyGFXAPIHandle&) = delete;
MoveOnlyGFXAPIHandle& operator=(MoveOnlyGFXAPIHandle&& other)
{
this->reset();
handle = other.handle;
other.handle = GFX_API_NULL_HANDLE;
return *this;
}
void reset() {
if (handle != GFX_API_NULL_HANDLE)
{
deleter(this->handle);
handle = GFX_API_NULL_HANDLE;
}
}
void assign(T handle) { reset(); this->handle = handle; }
operator T() const { return handle; }
T get() const { return handle; }
~MoveOnlyGFXAPIHandle() {
reset(); }
private:
T handle;
};
using GFXAPIShaderModuleHandle = MoveOnlyGFXAPIHandle < int,
+[](int handle) {
//destroyShaderModule(handle);
} > ;
struct ShaderModuleObject {
GFXAPIShaderModuleHandle handle;
};
int main()
{
ShaderModuleObject module_object;
return 0;
}
O que são tipos com ligação interna e externa? E por que meu tipo tem ligação interna, e por que isso é algo ruim a ponto de o GCC me alertar sobre isso?
Aqui está o MCVE:
O que está acontecendo aqui? O tipo com alias de
Bar
possui ligação interna, pois pode ser referenciado apenas pelo nome da unidade de tradução atual. Isso acontece porque ele usa lambda (que, aliás, é anônimo, não pode ser referenciado pelo nome e, portanto, não possui ligação) e a inclusãoheader.h
em uma unidade de tradução diferente resultará na produção de um tipo diferente e na violação do ODR paraFrob
.Este exemplo é bastante inofensivo (sem problemas, já que há apenas uma unidade de tradução), mas em cenários mais complexos, isso pode levar a bugs estranhos e mensagens de erro confusas. Por exemplo, se
Bar
for usado como parte da interface DLL, o nome do símbolo corrompido pode ser diferente, dependendo da unidade de tradução onde o cabeçalho está incluído, produzindo erros de linker crípticos:Outro MCVE onde esse tipo de problema resulta em um erro HARD em vez de um aviso: