I have a struct template like the following:
// 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); }
};
Then, I inherit a non-template struct, ConcreteS, from S<A>; struct A
being incomplete at this point, as I only forward declare it in
ConcreteS.h:
// ConcreteS.h
#pragma once
#include "S.h"
struct A;
struct ConcreteS : public S<A>
// struct A incomplete here ^
{
ConcreteS();
~ConcreteS() override;
};
I include A.h in ConcreteS.cpp to make the definition of struct A visible
to the implementation of ConcreteS's destructor:
// ConcreteS.cpp
#include "ConcreteS.h"
#include "A.h"
#include <cstdio>
ConcreteS::ConcreteS() { std::puts("ConcreteS()"); }
ConcreteS::~ConcreteS() { std::puts("~ConcreteS()"); }
Finally, I instanciate ConcreteS in function main:
// main.cpp
#include "ConcreteS.h"
int main()
{
ConcreteS concreteS{};
}
The above code compiles (and runs) fine with:
- GCC 11.3.0;
- Clang 14.0.0.
The output is:
ConcreteS()
~ConcreteS()
But fails to compile on:
- Visual Studio 2019 (compiler: MSVC 19.29.30133.0);
- Visual Studio 2013 (compiler: MSVC 18.0.40629.0).
Here you are the error message from VS13 (the one from VS19 is similar):
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...
Question: Who is right? Visual Studio or GCC/Clang?
For reference, I also post the declaration and definition of 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()