I am facing a compilation error with one gcc version (4.3.2), whereas the same code will be compiled without complaints with a newer version of gcc, e.g. 4.5.2.
The following example illustrates the problem:
class Base {
protected:
int member;
};
template<typename T>
class A : public Base {
};
template<typename T>
class C : public A<T> {
C() {
Base::member = 1;
}
};
int main() {
}
For 4.3.2 I am getting:
test.cpp: In constructor 'C<T>::C()':
test.cpp:4: error: object missing in reference to 'Base::member'
test.cpp:14: error: from this location
For 4.5.2 it compiles just fine.
If one compiler version accepts the code it cannot be completely wrong. My guess is that it conforms to the C++ standard, but the older compiler was lacking a proper implementation of the same.
What exactly is the problem?
Is there a portable way of writing that kind of code such that as much compilers as possible accept it?
This is definitely a bug in the earlier version of g++ but the solution is simple: Just add this-> to the expression, as this->Base::member = 1; which unconfuses the compiler.
Related
I am trying to port a code that should work and compile on both Linux (GCC/C++) and windows (MSVC)
however the following line is giving me trouble
template <class TDerived>
struct Event
{
inline static std::string eventId = typeid(TDerived).name();
};
struct Derived : public Event<Derived>
{
Derived() = default;
};
The code uses typeid and name() to set the event name,
In GNU/C++ it compiles properly in both Linux and Apple (Clang), and even in Windows using MingW.
but in windows using MSVC
it gives the following error
error C2027: use of undefined type 'Derived'
message : see declaration of 'Derived'
message : see reference to class template instantiation 'Event<Derived>' being compiled
basically because Derived is incomplete at this time and is not visible in the Event scope.
But why does GNU/GCC, MingW and Clang succesfuly compiled it? Do we have a workaround in MSVC (Visual Studio)?
C++ standard says:
[expr.typeid]
When typeid is applied to a type-id ... If the type of the type-id is a class type or a reference to a class type, the class shall be completely-defined.
As far as I can tell, the class isn't completely-defined in that context. If that interpretation is correct, then the rule is violated and the program is ill-formed. That said, I'm not 100% sure since template instantiation makes the rules quite complex.
But why does GNU/GCC, MingW and Clang succesfuly compiled it?
For whatever reason, their implementation can apparently cope with non-completely defined classes. They aren't required to successfully compile it. Technically they do not conform to the standard if they do not provide a diagnostic message (assuming the the interpretation of program being ill-formed is correct).
Do we have a workaround in MSVC (Visual Studio)?
Defining a non-inline static member seems to work in godbolt:
template <class TDerived>
struct Event
{
static std::string eventId;
};
template <class TDerived>
std::string Event<TDerived>::eventId = typeid(TDerived).name();
Note that the name is implementation defined and is not going to be the same across all language implementations.
I found a work around and it works.
template <class TDerived>
struct Event
{
private:
static std::string getEventId() { return typeid(TDerived).name(); }
public:
inline static std::string eventId = getEventId();
};
I have code like this:
class A{
public:
int b;
};
int main()
{
A a{.b = 5};
}
And the program compiles.
However, when I add another class and I make A inherit that class (like below), it throws an error "no matching function for call to ‘A::A()’" (or "No matching constructor for initialization of A").
class C{
};
class A: public C{
public:
int b;
};
int main()
{
A a{.b = 5};
}
Why?
You are using feature "Designated initializers" which is available since C++20.
Also I can't reproduce this issue: https://godbolt.org/z/fz3PeP
note with C++17 gcc and clang just file an warning, msvc file an error
with C++20 it is fine on all three (msvc needs c++latest option).
with C++14 it files error everywhere
So looks like problem is just compiler version or configuration
I have the following struct MyStruct that calls Foo<int> on construction:
struct MyStruct {
MyStruct() {
Foo<int>();
}
template<typename T>
void Foo() {
[]() {
struct Base {
Base(int n) {}
};
struct Derived : Base {
// using Base=Base; // needs to be uncommented for Apple LLVM version 9.1.0 (clang-902.0.39.2)
Derived() :
Base(0) // problematic line
{}
} derived;
};
}
};
I tried to compile this with clang (via godbolt, command line params --std=c++1y):
3.6: error: type 'Base' is not a direct or virtual base of 'Derived' (see problematic line in code above)
3.7 and newer: compiles (with a warning about unused expressions)
So I expected that with newer versions this should be ok. However, when I tried to compile it on a Mac using XCode9 (which, according to this, uses clang 4.0 (clang --version gives Apple LLVM version 9.1.0 (clang-902.0.39.2))), I get an error from the same line as issued by clang 3.6:
error: member initializer 'Base' does not name a non-static data member or base class
As a workaround, I tried introducing using Base=Base within struct Derived (as indicated), which made it compile via XCode. (As a sidenote: Using the same workaround did not work for clang 3.6 on godbolt.)
So here are my questions:
Is this a compiler bug? If yes: Is it documented somewhere?
Is my workaround using Base=Base valid, well-defined C++?
Following code is a hypothetical code. This is a perfectly valid code under g++ (4.2.1). When compiled with Clang++ (4.2) it produces error as qualified reference to 'myclass' is a constructor name rather than a type wherever a constructor can be declared
class myclass
{
public:
myclass() { }
~myclass() {}
};
myclass::myclass* funct() {
return new myclass();
}
I could fix this by changing myclass::myclass* to myclass*. However I am not expected to change any code. Is there any commandline flags that I could provide in order to compile this code as is using Clang++ ?
No, there is no such flag : since this program is ill-formed, it should not compile.
If a compiler compiles it, then it is a compiler bug. This bug report looks like the one impacting your particular gcc version.
The code should be fixed to :
myclass* funct() {
return new myclass();
}
gcc 4.9.1 also rejects the code:
error: ‘myclass::myclass’ names the constructor, not the type
Unless the code base is happy with the rather old gcc 4.2, I see no alternative than to fix the code.
I'm trying to compile the MSVC standard library using Clang. But it fails because the standard library uses explicit template function specialization at class scope.
This is a MS extension, and apparently not available in Clang.
Here's a simple example that compiles fine with MSVS, but not with Clang.
template<class T>
class A
{
public:
A()
{
foo((T)0, 0);
}
template<class T2>
void foo(T2, void* p) {}
template<>
void foo<bool>(bool, void* p)
{
t = (T)p;
}
T t;
};
int main()
{
A<bool> a;
return 0;
}
What should I do to get this feature to work when using Clang, so I can compile the MSVC standard library?
This is the compile error I get:
warning: explicit specialization of 'foo' within class scope is a Microsoft extension [-Wmicrosoft]
void foo<bool>(bool, void* p)
error: expected ';' after expression
t = (T)p;
^
;
error: no member named 'T' in 'A<bool>'
t = (T)p;
^
I think you'll have three possibilities:
Rewrite the MSVC library so it does not use that extension (could be fun, but time consuming...)
Fix that MS-extension in Clang (could be even more fun and time consuming...)
Do the sane thing: use each library with the compiler it was made for (how boring...)
Update from this testing code for Clang I assume that it is already implemented, but it does not expect an explicit template parameter:
template<>
void foo(bool, void* p)
{
t = (T)p;
}
Its probably a bug in Clang's implementation of that extension.
AFAIK you will have to extend clang to support explicit specialization within class scope. I don't think there is any way around it, unless you want to modify the library.