Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A 'using' statement compiles with g++, fails compilation with clang

I have code of the following structure (which is of course much more complex in reality, especially "Base" is a three-liner, but I've tried to capture the gist of it):

template <class T>
class A {};

template <class T>
class B {
public:
    B(){};
};

template <class T>
class C : public B<A<T>> {
public:
    using Base = B<A<T>>;
    using Base::B;
};

static const C<int> c{};

The code compiles fine with g++ via

g++ -c test.cpp -std=c++11

However, with clang++ I get an error message I don't really understand

clang++ -c test.cpp -std=c++11

test.cpp:14:14: error: dependent using declaration resolved to type without 'typename' using Base::B;

Is there anything wrong with my code or is this a bug in clang?

Note: When writing using B<A<T>>::B; it compiles fine with both compilers, but this not a real solution to my problem.

Edit: clang version is 3.5.0, gcc version is 4.9.2

like image 257
Andreas Stöckel Avatar asked Jan 15 '15 00:01

Andreas Stöckel


2 Answers

This case has been discussed in the C++ committee (according to Richard Smith https://llvm.org/bugs/show_bug.cgi?id=23107#c1) and things have gotten clearer now:

using Base::B

is not intended to be valid code.

The following method is the correct way to express constructor inheritance when introducing a class alias Base:

using Base::Base

However, the error messages produced by clang are misleading and will hopefully be solved as part of this bug report (https://llvm.org/bugs/show_bug.cgi?id=22242).

like image 166
Andreas Stöckel Avatar answered Nov 13 '22 13:11

Andreas Stöckel


From section 12.1:

Constructors do not have names

So the usual rules for qualified lookup do not apply. Instead you have to rely on the special rule for constructor lookup (section 3.4.3.1):

In a lookup in which function names are not ignored and the nested-name-specifier nominates a class C:

— if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C, or

in a using-declaration (7.3.3) that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id’s template-name in the last component of the nested-name-specifier,

the name is instead considered to name the constructor of class C.

So you can certainly write

using Base::Base;

instead of

using Base::B;

Your original version should work under the first bullet point, but injected-class-names get complicated when templates are involved. Just go with the simpler version Base::Base, which additionally is more readable. Anyone who sees that instantly knows you are naming a constructor.

like image 27
Ben Voigt Avatar answered Nov 13 '22 12:11

Ben Voigt