Nikolay Amiantov Nikolay Amiantov - 1 year ago 51
C++ Question

Friend class, inheritance and typedef -- which behavior is correct?

I've stumbled upon an error which manifestates itself only on GCC 6.2.0, but not on Clang 3.9.0 (both in

mode). I'm unsure which behavior is correct (and whether I should file a bug).

Here's the code:

template<typename type_t>
class foo_t

class bar_t
using foo_t = int;

class baz_t:
public bar_t
template<typename type_t>
friend class foo_t;

On GCC this gives the following error:

test.cpp:17:15: error: using typedef-name ‘using foo_t = int’ after ‘class’
friend class foo_t;
test.cpp:9:19: note: ‘using foo_t = int’ has a previous declaration here
using foo_t = int;

From what I know of C++ standard, parent
's (or
s) should not leak into scope of the child and you need to explicitly qualify the name: see for example Propagating 'typedef' from based to derived class for 'template'. So it seems to me that GCC is incorrect here, but I'm not so sure of my C++ knowledge to say with confidence.

Thanks for any help!

Answer Source

From what I know of C++ standard, parent typedef's (or usings) should not leak into scope of the child and you need to explicitly qualify the name

That is incorrect. Members (including type aliases) declared in base classes are visible in derived classes normally. The question you linked to specifically deals with templates with a dependent base class, where two-phase name lookup applies (and again, applies to everything, not just type aliases).

This aside, the relevant part of the standard is C++14 (N4140) [dcl.type.elab] (emphasis mine):

3.4.4 describes how name lookup proceeds for the identifier in an elaborated-type-specifier. If the identifier resolves to a class-name or enum-name, the elaborated-type-specifier introduces it into the declaration the same way a simple-type-specifier introduces its type-name. If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.

(Note: elaborated-type-specifier is the construct class T for some T that we're dealing with).

3.4.4, in turn, says that when resolving the identifier in an elaborated-type-specifier into a name, non-type names are ignored (but type names are found normally).

GCC is therefore actually right, since the typedef-name foo_t in scope bar_t is "closer" in scope than the global-scope template-name foo_t. The unqualified name foo_t inside baz_t therefore resolves to bar_t::foo_t, which is a typedef-name and therefore makes the elaborated-type-specifier ill-formed.

The problem is with the resolution of the unqualified name foo_t. As you note yourself in comments to the question, explicitly stating which foo_t you mean should solve the issue:

tempalte <typename type_t>
friend class ::foo_t;