Nested class hidden by multiple inheritance
Asked Answered
L

1

7

Is this code valid C++(11)?

struct Base {
    template <typename>
    struct nested;
};
struct Derived1 : Base { };
struct Derived2 : Base { };
struct Derived3 : Derived1, Derived2 { };

typedef Derived3::nested<int> xxx;

What I know

The above code fails to compile with:

  • Apple LLVM 5.0 (clang-500.2.75)
  • Clang 3.4

But it successfully compiles with:

  • gcc 4.9.0 20131110 (experimental)
  • gcc 4.8

Also, if I change the nested type to a non-template type, i.e.

struct Base {
    struct nested;
};
...
typedef Derived3::nested xxx;

then it works with the above compilers.

[edit] Changing the nested template struct to a template alias also does not change anything;

template <typename> struct dependent { struct type; };
struct Base {
    template <typename T>
    using nested = typename dependent<T>::type;
};

produces the same results with the above compilers. [end edit]

From N3242 §10.1 [class.mi]

A class can be an indirect base class more than once and can be a direct and an indirect base class. There are limited things that can be done with such a class. The non-static data members and member functions of the direct base class cannot be referred to in the scope of the derived class. However, the static members, enumerations and types can be unambiguously referred to.

I think it means that the code should be valid, but I'm not sure.

Lethe answered 14/11, 2013 at 19:1 Comment(1)
I looked for the bug report (id 17929) filled by the OP as the bug(?) seems still there in clang 5.0.1. Unfortunately, there haven't been any answers.Walcoff
P
0

It is fine GCC is either right/more helpful (it sticks to the standard VERY strongly)

It can't see why the definition would be ambiguous because you're talking about a type not a member, and type are equal if their names are equal in C++ (names being some mangled form of types involved and such)

Addendum:

It'd be wrong if "nested" and "nested" in the other base were different. It is a struct, not a typedef or using (which are scoped)

GCC will whine if something is ambiguous, try with -pedantic if you want to make it bitch even where it is not. I see no reason why this should be rejected even if GCC is just being permissive.

Phonation answered 14/11, 2013 at 19:5 Comment(12)
I'm not sure I understand your addendum. Do you mean that the code could be ambiguous if nested was, for example, a C++11 template alias instead of a template struct?Lethe
@LouisDionne yes, because they could alias different things. They are treated like members during parsing because the same rules apply, the diamond problem rears its head with which alias you wanted to use.Phonation
Sorry for the delay; I was trying stuff locally and looking at Clang's Bugzilla. Please see my edit. Also, I'll open a bug on Clang and see what's up according to them.Lethe
@LouisDionne do you know what a "dependent lookup" is? If I have a constexpr function with a static_assert that always fails without a template param I cannot compile, if that constexpr is a template, until I try and use it somwhere the assert wont trip. In your edit the alias doesn't depend on anything that can change between the two, so GCC sees no ambiguity (because there isn't any)Phonation
Can you please post a snippet of code where GCC sees an ambiguity? Also, please take a look at the re-edit, although I'm not sure that is what you were referring to in your first comment.Lethe
@LouisDionne it'd be very hard to create. A "pure" function is a function with no side-effects (no statics, doesn't change the inputs) you cannot create an ambiguity with them when they have no params, because they will always return the same thing. Because in your second edit struct Base { template <typename T> using nested = typename dependent<T>::type; }; nested is pure (it itself is a template so nested<T>'s actual type is pure with no inputs) there can be no ambiguityPhonation
@LouisDionne to introduce ambiguity would be very tedious if possible... due to the pure nature of templates I don't think it is possible. Because you can't store a result pass it to a "function" and change that thing stored (constexpr by definition, static const.... ) and any type traits set by a template paramater belong to that type<T>.Phonation
@LouisDionne to see ambiguity add a member "int evil;" or something, then try and access Derived3::evil, I dare you :P (the ambiguity is do you want Derived2::Base::evil or Derived2::Base::evil, it is very easy to get them to have different values)Phonation
I do understand how an ambiguity could arise if nested was not a type. However, my question is how can an ambiguity arise if nested is a type (a template, an alias or anything else). Your previous comments seem to suggest that it is not possible. I'll wait for the reaction on the Clang side before accepting (or not) your answer.Lethe
@LouisDionne it is not possible to validly cause an ambiguity with a type name. Validly meaning without lying to C++ in different files. Otherwise a type could have a different trait based on where it is seen, which is wrong, remember a type is a template part too. Each instantiation is a different type. Clever GCC knows this, I just proved it is a contradiction on paper.Phonation
Still waiting. I'll update whenever I have an answer from clang's side.Lethe
@LouisDionne whatever, I've proved the proposition.Phonation

© 2022 - 2024 — McMap. All rights reserved.