我有一個結構模板,如下所示:
// S.h
#pragma once
#include <vector>
template <typename T>
struct S
{
std::vector<T*> ts;
virtual ~S() { for (auto* t : ts) t->foo(); }
void attach(T& t) { this->ts.push_back(&t); }
};
ConcreteS然后,我從;繼承了一個非模板結構S<A>; struct A
在這一點上是不完整的,因為我只轉發宣告它
ConcreteS.h:
// ConcreteS.h
#pragma once
#include "S.h"
struct A;
struct ConcreteS : public S<A>
// struct A incomplete here ^
{
ConcreteS();
~ConcreteS() override;
};
我包括A.h使' 的解構式的實作可見ConcreteS.cpp的定義:struct AConcreteS
// ConcreteS.cpp
#include "ConcreteS.h"
#include "A.h"
#include <cstdio>
ConcreteS::ConcreteS() { std::puts("ConcreteS()"); }
ConcreteS::~ConcreteS() { std::puts("~ConcreteS()"); }
ConcreteS最后,我在函式中實體化main:
// main.cpp
#include "ConcreteS.h"
int main()
{
ConcreteS concreteS{};
}
上面的代碼編譯(和運行)很好:
- GCC 11.3.0;
- 鏗鏘聲14.0.0。
輸出是:
ConcreteS()
~ConcreteS()
但無法編譯:
- Visual Studio 2019(編譯器:MSVC 19.29.30133.0);
- Visual Studio 2013(編譯器:MSVC 18.0.40629.0)。
這是來自 VS13 的錯誤訊息(來自 VS19 的錯誤訊息類似):
Microsoft (R) Build Engine version 12.0.40629.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.
Checking Build System
Building Custom Rule <proj_path>/CMakeLists.txt
cl : Command line warning D9002: ignoring unknown option '/permissive-' [<proj_path>\build_vs13\tmp.vcxproj]
A.cpp
ConcreteS.cpp
main.cpp
<proj_path>\S.h(10): error C2027: use of undefined type 'A' [<proj_path>\build_vs13\tmp.vcxproj]
<proj_path>\ConcreteS.h(5) : see declaration of 'A'
<proj_path>\S.h(10) : while compiling class template member function 'S<A>::~S(void)'
<proj_path>\ConcreteS.h(8) : see reference to class template instantiation 'S<A>' being compiled
<proj_path>\S.h(10): error C2227: left of '->foo' must point to class/struct/union/generic type [<proj_path>\build_vs13\tmp.vcxproj]
Generating Code...
問題:誰是對的?Visual Studio 還是 GCC/Clang?
struct A作為參考,我還發布了and my
的宣告和定義CMakeLists.txt:
// A.h
#pragma once
struct A
{
void foo() const;
};
// A.cpp
#include "A.h"
#include <cstdio>
void A::foo() const { std::puts("A::foo()"); }
# CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
project(tmp)
add_executable(tmp A.cpp A.h ConcreteS.cpp ConcreteS.h S.h main.cpp)
if (MSVC)
target_compile_options(tmp PRIVATE /W4 /WX /permissive-)
else()
target_compile_options(tmp PRIVATE -Wall -Wextra -pedantic -Werror)
endif()
uj5u.com熱心網友回復:
用作S<A>基類會導致它被隱式實體化。
A通常這不會成為問題,因為您的類在實體化時不需要完整。
但是,您的解構式的定義S<A>需要A完整(因為成員訪問)。這通常也不是問題,因為成員函式的定義通常不會使用類模板特化的隱式實體化來隱式實體化,但只有在需要定義存在的背景關系中或在解構式的情況下使用它們時它可能被呼叫。
但是,您的解構式是virtual. 特別是對于virtual成員函式,是否使用包含類的隱式實體化來實體化它們是未指定的。( [temp.inst]/11 )
因此,實作可能會或可能不會選擇S<A>::~S<A>在翻譯單元中為main.cpp. 如果是這樣,程式將無法編譯,因為定義中的成員訪問對于不完整型別的格式不正確。
換句話說,程式是否有效以及所有提到的編譯器是否符合標準都是未指定的。
如果您在解構式上洗掉virtual(and override),則S<A>允許的解構式的唯一實體化將在完整且實體化有效的ConcreteS.cpp翻譯單元中。A該程式是有效的,它也應該在 MSVC 下編譯。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/473687.html
