I'm working on a hobby programming language that transpiles to C (still very alpha quality: https://github.com/ehren/cmile).
I'm working on inheritance and constructors currently. The C code below writes the derived classes in a way that's agnostic as to whether their base class is a template or not (this simplifies various things in my compiler).
Note that inheriting from a decltype
works in all 3 compilers. But calling a base class constructor using decltype
in the initializer list e.g. Child(const T& t) : decltype(Base(std::declval<T>())(t) {}
only works with g and clang -std=c 20 but not msvc19 /std:c 20
Here's the complete example that fails in MSVC with "error C2059: syntax error: 'type'" but works in clang or gcc:
#include <utility>
template <typename T>
struct Generic {
T x;
explicit Generic(const T& x) : x(x) {};
};
struct Concrete {
int x;
explicit Concrete(int x) : x(x) {};
};
template <typename T>
struct GenericChild : decltype(Generic(std::declval<T>())) {
// explicit GenericChild(const T& t) : Generic(t) {}; // rejected by all 3 compilers (very annoying that CTAD doesn't work here)
// explicit GenericChild(const T& t) : Generic<T>(t) {}; // explicitly calling base constructor as a template works fine in all compilers (but requires more tracking in my transpiler)
explicit GenericChild(const T& t) : decltype(Generic(std::declval<T>())) (t) {}; // my desired solution: why does this fail with MSVC only?
};
template <typename T>
struct ConcreteChild : decltype(Concrete(std::declval<T>())) {
// explicit ConcreteChild(const T& t) : Concrete(t) {}; // this is fine of course
explicit ConcreteChild(const T& t) : decltype(Concrete(std::declval<T>())) (t) {}; // "base class is a template?" agnostic version. rejected by MSVC.
// aside: ^ removing both constructors here ICEs MSVC
};
int main() {
auto f = Generic(5);
auto f2 = GenericChild(5);
auto f3 = Concrete(5);
auto f4 = ConcreteChild(5);
}
or link: https://godbolt.org/z/P7Wh99qT9
Which compiler is in the right to accept/reject this code?
Is there a change I can make so MSVC will accept the decltype in the initializer list? (without having to know whether the Base constructor must be called explicitly as Base<T>(t)
or Base(t)
). I suppose there are various tricks that can be employed to explicitly check if Base is a template (but I'm not sure how straightforwardly they'll apply to the initializer list base class constructor call case if the decltype approach doesn't work).
Just as an aside, the C above (part of it; also ignoring refcounting and other details) would look like this in my language. The need for a construcor call in the initializer list (in the transpiled C output) will be inferred from the presence of the super.init call in the transpiled language's constructor:
class (Generic:
x
)
class (GenericChild(Generic):
def (init, x:
super.init(x)
)
)
def (main:
f = Generic(5)
f2 = GenericChild(5)
)
CodePudding user response:
Clang and GCC are right. [class.base.init]/3 says:
A mem-initializer-list can initialize a base class using any class-or-decltype that denotes that base class type.
I can't prove that there's not some other rule in there that makes your example illegal, but I strongly suspect that MSVC is just having trouble with parsing. Parsing ctor-initializers is really hard.