The following code
#include <vector>
#include <string>
template<typename T>
struct V : public std::vector<T>
{
using Impl = std::vector<T>;
using typename Impl::vector; // the constructors
};
int main()
{
std::string empty;
V<std::string> meow{42UL, empty};
}
Is compiled fine by GCC 8.2 (calls the size_t, string constructor).
However, clang up to 14 rejects it with
<source>:14:20: error: no matching constructor for initialization of 'V<std::string>' (aka 'V<basic_string<char>>')
V<std::string> meow{42UL, empty};
^ ~~~~~~~~~~~~~
<source>:5:8: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided
struct V : public std::vector<T>
^
<source>:5:8: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided
<source>:5:8: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 2 were provided
as if V had no constructors: https://godbolt.org/z/M91zb6Pjr
Replacing using typename Impl::vector; with using Impl::Impl; makes clang accept the code. What is going on here?
Since the recent resolution of CWG issue 2070 it is not possible anymore to use a dependent alias to inherit the constructor in a
usingdeclaration, except if repeating the name of the alias.You must use the same identifier to name the base class as you are using to refer to the constructor (the last unqualified-id after the nested-name-specifier), for example:
or making use of the lookup of the injected class name of the base class in
V:or by a special rule for using-declarators the name of the alias may also be repeated instead of using the injected class name if it is dependent:
(see https://eel.is/c++draft/basic#class.qual-1.2 and https://eel.is/c++draft/namespace.udecl#1.sentence-3)
If you write
using typename Impl::vector;instead it is not inheriting a constructor, but insteadvectoris going to be interpreted as a type named by the injected class name invector<T>, which then is imported as the namevectorinto the class scope.This requirement is to avoid that the same
usingline will cause the constructor to be inherited sometimes and a type name to be imported other times, depending on the specialization of the template.Basically, after the resolution you know that the
usingdeclaration with a dependent nested-name-specifier is inheriting a constructor if and only if it is ending inA::AorA::Afor some nameA(potentially with additional template argument lists, etc.).It seems that Clang has always implemented it this way, although I think it wasn't really correct according to the standard before the resolution of the issue.