Consider a struct B derived from two structs A<1> and A<2>, each defining a typename D to itself, and there is also a global typename D. Then one writes .D:: after a B object , which D must be taken?
template<int N> struct A{ int v = N; using D = A; };
struct B : A<1>, A<2> {};
using D = A<1>;
int main() { return B{}.D::v; }
According to the standard:
[basic.lookup.classref]
4 If the id-expression in a class member access is a qualified-id of the form
class-name-or-namespace-name::...
the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the object expression ([class.member.lookup]) and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression.
So, first D must be looked inside struct B, but it is ambiguous there. And indeed, Clang reports an error about it:
error: member 'D' found in multiple base classes of different types
But GCC accepts the code, demo: https://gcc.godbolt.org/z/74qraoeac
It seems that GCC interprets the standard as if D is ambiguous in B, let us look outside of B. Which compiler is correct here?
Clang is correct according to the recent clarification of that wording, which now says
If nothing is found by qualified lookup for a member-qualified name that is the terminal name ([expr.prim.id.qual]) of a nested-name-specifier and is not dependent, it undergoes unqualified lookup.
Now there is no problematic “the name, if found” which could imply uniqueness.
Related
Consider a example in the standard
Example
template<class T> struct A {
typedef int M;
struct B {
typedef void M;
struct C;
};
};
template<class T> struct A<T>::B::C : A<T> {
M m; // OK, A<T>::M
};
The comment says M refer to A<T>::M, I doubt with this, because of these rules:
temp.dep#3
In the definition of a class or class template, the scope of a dependent base class is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
That means a name in the scope of dependent base class is never be considered during unqualified name lookup.
Name M is an unqualified name. Hence M declared in A<T> is not considered.
Then according to the rule for unqualified name lookup, that is:
basic.lookup.unqual#8
For the members of a class X, a name used in a member function body, in a default argument, in a noexcept-specifier, in the brace-or-equal-initializer of a non-static data member, or in the definition of a class member outside of the definition of X, following the member's declarator-id32, shall be declared in one of the following ways:
if X is a nested class of class Y, shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y's enclosing classes, starting with the innermost enclosing class)
Since C is a nested class of B, Hence I think lookup shall be began at B, then A, due to there's a name M in the scope of B, hence the lookup shall be stoped.
In all the cases listed in [basic.lookup.unqual], the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
So, according to these rules, the name M within A<T>::B::C shall refer to B::M.
The outcome is here.
GCC agreed what the standard said, however clang reported an error and denoted that the type M is void. The outcome of clang consistent with my analysis. According to these reasons, I agree clang is right.
So, I wonder is it a defect? Or what something I misunderstand ?
According to C++ Defect Report Support in Clang, currently (2020-07-06) Clang does not implement the resolution of CWG591, where the paragraph with the definition of dependent base class and the Example you cite in the question was added.
Consider the following code:
#include <iostream>
struct a {
virtual void f() = 0;
};
struct b: a {
private:
void f() override {
std::cout << "b::f()\n";
}
};
struct c: b {
public:
using a::f;
};
int main() {
::c c;
c.f();
}
This compiles and works as expected with g++, clang and msvc, i.e., print b::f().
However, if I replace a and b by this:
struct a {
void f() { }
};
struct b: a {
private:
using a::f;
};
...the code does not compile anymore with gcc but compiles fines with both clang, clang-cl and msvc (thanks StoryTeller and Adrian Mole). I get the following error (on the using a::f; line in c):
error: 'void a::f()' is inaccessible within this context
I cannot find a clear point in the standard about the behavior of using a::f; (in c) in these cases, so are the above well-defined by the standard?
Note: I am not talking about bringing something in the public scope of a class (like using b::f in c if b::f was protected), but really making members from the top-level class accessible in the most-derived class after these have been made inaccessible in an intermediate base class.
I believe GCC is wrong to reject the modified code.
[namespace.udecl]
1 Each using-declarator in a using-declaration introduces a set
of declarations into the declarative region in which the
using-declaration appears. The set of declarations introduced by the
using-declarator is found by performing qualified name lookup
([basic.lookup.qual], [class.member.lookup]) for the name in the
using-declarator, excluding functions that are hidden as described
below.
3 In a using-declaration used as a member-declaration, each
using-declarator's nested-name-specifier shall name a base class of
the class being defined. If a using-declarator names a constructor,
its nested-name-specifier shall name a direct base class of the class
being defined.
So first I'd note that paragraph 3 makes a distinction between a base and a direct base. Therefore we can name a::f in a using declaration. Secondly, according to paragraph 1, name lookup proceeds as one would expect
[class.qual]
1 If the nested-name-specifier of a qualified-id nominates a
class, the name specified after the nested-name-specifier is looked up
in the scope of the class ([class.member.lookup]), except for the
cases listed below. The name shall represent one or more members of
that class or of one of its base classes (Clause [class.derived]).
[class.member.lookup]
1 Member name lookup determines the meaning of a name
(id-expression) in a class scope. Name lookup can result in an
ambiguity, in which case the program is ill-formed. For an
id-expression, name lookup begins in the class scope of this; for a
qualified-id, name lookup begins in the scope of the
nested-name-specifier. Name lookup takes place before access control.
So a::f is to be looked up only in the scope of a or its own base classes. It should not be looked up at all in b. I would argue that therefore the accessibility of the name f in b should not affect the accessibility of the name f in a when doing qualified name lookup.
In a, f is public. And so can be named by a qualified-id in any declarative region where a may be named. That includes c. And so the using declaration uses a valid name for a valid member of a base class. That is accessible in that declarative region. It is therefore valid.
As another data point, GCC has no problem with the accessibility of a::f in other uses. For example, GCC allows forming a pointer to member to a::f inside the scope of c.
struct c: b {
public:
c() {
[[maybe_unused]] auto f = &a::f;
};
};
So it clearly does not consider the name a::f inaccessible in all contexts due to b.
This question already has an answer here:
Why are redundant scope qualifications supported by the compiler, and is it legal?
(1 answer)
Closed 6 years ago.
I noticed by accident that this code compiles and works correctly:
struct M { int some_int; };
static_assert(std::is_same<
decltype(M::M::M::M::some_int) /* <- this */,
int>::value, "Types must be int");
Why is this correct (decltype(M::M::M::M::some_int) <=> decltype(M::some_int))?
What other constructs one can use this pattern with class::class::...::member?
Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23824.1 for x86
This is valid in all contexts, not just decltype. A class contains its own name as the injected class name. So within a class A::B::M, the name M is injected to refer to the class A::B::M. This means that you can then use M::M::M::some_member to refer to members of that class, if you really want to.
[Live example]
Note that when referring just to the class name itself (e.g. M::M::M), the situation is slightly different. If such a reference occurs in a place where a reference to a function could also potentially be correct, the syntax is taken to refer to the constructor instead. However, in type-only contexts, even such reference is valid. Example:
M::M::M m; // illegal, M::M interpreted as reference to constructor
struct D : public M::M::M // legal, a function could not be references here, so M::M::M means M
{};
This works because of the injected-class-name:
(N3337) [class]/2:A class-name is inserted into the scope in which it is declared immediately after the class-name is seen.
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]
So you can arbitrarily nest these, and they'll work with derived types as well:
struct A { using type = int; };
struct B : public A {};
using foo = B::B::B::A::A::A::type;
Note that in the case of A[::A]*::A, the injected-class-name can be considered to name the constructor instead:
[class.qual]/2: In a lookup in which the constructor is an acceptable lookup result 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 (Clause 9), or
— [...]
the name is instead considered to name the constructor of class C.
The following code is causing a little headache for us: clang and MSVC accepts the following code, while GCC rejects it. We believe GCC is right this time, but I wanted to make it sure before filing the bugreports. So, are there any special rules for operator[] lookup that I'm unaware of?
struct X{};
struct Y{};
template<typename T>
struct B
{
void f(X) { }
void operator[](X){}
};
template<typename T>
struct C
{
void f(Y) { }
void operator[](Y){}
};
template<typename T> struct D : B<T>, C<T> {};
int main()
{
D<float> d;
//d.f(X()); //This is erroneous in all compilers
d[Y()];//this is accepted by clang and MSVC
}
So is the above code is correct in resolving the operator[] call in the main function?
It's not 100% clear in which compiler the issue lie. The standard goes over a lot of rules for name lookup (which is what this is an issue with), but more specifically section 13.5.5 covers the operator[] overload:
13.5.5 Subscripting [over.sub]
1 - operator[] shall be a non-static member function with exactly one parameter. It implements the subscripting syntax
postfix-expression [ expr-or-braced-init-list ]
Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T if T::operator[](T1) exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3.3).
Looking at the standard on Overloading (chapter 13):
13 Overloading [over]
1 - When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded. By extension, two declarations in the same scope that declare the same name but with different types are called overloaded declarations. Only function and function template declarations can be overloaded; variable and type declarations cannot be overloaded.
2 - When an overloaded function name is used in a call, which overloaded function declaration is being referenced is determined by comparing the types of the arguments at the point of use with the types of the parameters in the overloaded declarations that are visible at the point of use. This function selection process is called overload resolution and is defined in 13.3.
...
13.2 Declaration matching [over.dcl]
1 - Two function declarations of the same name refer to the same function if they are in the same scope and have equivalent parameter declarations (13.1). A function member of a derived class is not in the same scope as a function member of the same name in a base class.
So according to this and section 10.2 on derived classes, since you've declared struct D : B, C, both B and C have member functions for operator[] but different types, thus the operator[] function is overloaded within the scope of D (since there's no using nor is operator[] overridden or hidden directly in D).
Based on this, MSVC and Clang are incorrect in their implementations since d[Y()] should be evaluated to d.operator[](Y()), which would produce an ambiguous name resolution; so the question is why do they accept the syntax of d[Y()] at all?
The only other areas I could see with regards to the subscript ([]) syntax make reference to section 5.2.1 (which states what a subscript expression is) and 13.5.5 (stated above), which means that those compilers are using other rules to further compile the d[Y()] expression.
If we look at name lookup, we see that 3.4.1 Unqualified name lookup paragraph 3 states that
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2.
Where 3.4.2 states:
3.4.2 Argument-dependent name lookup [basic.lookup.argdep]
1 - When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found.
2 - For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:
...
(2.2) - If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces.—end note ]
Note the emphasis on may.
With the above points and a couple of others from 3.4 (name lookup), one could believe that Clang and MSVC are using these rules to find d[] first (and thus finding it as C::operator[]) vs. using 13.5.5 to turn d[] into d.operator[] and continuing compilation.
It should be noted that bringing the operators of the base classes into scope of the D class or using explicit scope does, however, 'fix' this issue across all three compilers (as is expected based on the using declaration clauses in the references), example:
struct X{};
struct Y{};
template<typename T>
struct B
{
void f(X) { }
void operator[](X) {}
};
template<typename T>
struct C
{
void f(Y) { }
void operator[](Y) {}
};
template<typename T>
struct D : B<T>, C<T>
{
using B<T>::operator[];
using C<T>::operator[];
};
int main()
{
D<float> d;
d.B<float>::operator[](X()); // OK
//d.B<float>::operator[](Y()); // Error
//d.C<float>::operator[](X()); // Error
d.C<float>::operator[](Y()); // OK
d[Y()]; // calls C<T>::operator[](Y)
return 0;
}
Since the standard is ultimately left to the interpretation of the implementer, I'm not sure which compiler would be technically correct in this instance since MSVC and Clang might be using other rules to compile this though, given the subscripting paragraphs from the standard, I'm inclined to say they are not strictly adhering to the standard as much as GCC is in this instance.
I hope this can add some insight into the problem.
I believe that Clang and MSVC are incorrect, and GCC is correct to reject this code. This is an example of the principle that names in different scopes do not overload with each other. I submitted this to Clang as llvm bug 26850, we'll see if they agree.
There is nothing special about operator[] vs f(). From [over.sub]:
operator[] shall be a non-static member function with exactly one parameter. [...] Thus, a subscripting expression x[y] is interpreted as x.operator[](y) for a class object x of type T
if T::operator[](T1) exists and if the operator is selected as the best match function by the overload
resolution mechanism
So the rules governing the lookup of d[Y()] are the same as the rules governing d.f(X()). All the compilers were correct to reject the latter, and should have also rejected the former. Moreover, both Clang and MSVC reject
d.operator[](Y());
where both they accept:
d[Y()];
despite the two having identical meaning. There is no non-member operator[], and this is not a function call so there is no argument-dependent lookup either.
What follows is an explanation of why the call should be viewed as ambiguous, despite one of the two inherited member functions seeming like it's a better match.
The rules for member name lookup are defined in [class.member.lookup]. This is already a little difficult to parse, plus it refers to C as the object we're looking up in (which in OP is named D, whereas C is a subobject). We have this notion of lookup set:
The lookup set for f in C, called S(f,C), consists of two component sets: the declaration set, a set of
members named f; and the subobject set, a set of subobjects where declarations of these members (possibly
including using-declarations) were found. In the declaration set, using-declarations are replaced by the set
of designated members that are not hidden or overridden by members of the derived class (7.3.3), and type
declarations (including injected-class-names) are replaced by the types they designate.
The declaration set for operator[] in D<float> is empty: there is neither an explicit declaration nor a using-declaration.
Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi,
and merge each such lookup set S(f,Bi) in turn into S(f,C).
So we look into B<float> and C<float>.
The following steps define the result of merging lookup set S(f,Bi)
into the intermediate S(f,C):
— If each of the subobject members of S(f,Bi) is a base class subobject of at least one of the subobject
members of S(f,C), or if S(f,Bi) is empty, S(f,C) is unchanged and the merge is complete. Conversely,
if each of the subobject members of S(f,C) is a base class subobject of at least one of the
subobject members of S(f,Bi), or if S(f,C) is empty, the new S(f,C) is a copy of S(f,Bi).
— Otherwise, if the declaration sets of S(f,Bi) and S(f,C) differ, the merge is ambiguous: the new
S(f,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent
merges, an invalid declaration set is considered different from any other.
— Otherwise, the new S(f,C) is a lookup set with the shared set of declarations and the union of the
subobject sets.
The result of name lookup for f in C is the declaration set of S(f,C). If it is an invalid set, the program is
ill-formed. [ Example:
struct A { int x; }; // S(x,A) = { { A::x }, { A } }
struct B { float x; }; // S(x,B) = { { B::x }, { B } }
struct C: public A, public B { }; // S(x,C) = { invalid, { A in C, B in C } }
struct D: public virtual C { }; // S(x,D) = S(x,C)
struct E: public virtual C { char x; }; // S(x,E) = { { E::x }, { E } }
struct F: public D, public E { }; // S(x,F) = S(x,E)
int main() {
F f;
f.x = 0; // OK, lookup finds E::x
}
S(x, F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D)
is discarded in the first merge step. —end example ]
So here's what happens. First, we try to merge the empty declaration set of operator[] in D<float> with that of B<float>. This gives us the set {operator[](X)}.
Next, we merge that with the declaration set of operator[] in C<float>. This latter declaration set is {operator[](Y)}. These merge sets differ, so the merge is ambiguous. Note that overload resolution is not considered here. We are simply looking up the name.
The fix, by the way, is to add using-declarations to D<T> such that there is no merge step done:
template<typename T> struct D : B<T>, C<T> {
using B<T>::operator[];
using C<T>::operator[];
};
Below is a purely academically invented class hierarchy.
struct X{
void f1();
void f2();
void f3();
};
struct Y : private X{
void f4();
};
struct Z : X{
};
struct D : Y, Z{
using X::f2;
using Z::X::f3;
};
int main(){}
I expected using declaration for X::f2 to be ambiguous as 'X' is an ambiguous base of 'D' (visbility vs accessibility of X). However g++ (ideone.com) compiles it fine.
I checked with Online Comeau and it gives error in using declaration for X::f2 as expected. However it gives ambiguity for using declaration for Z::X::f3 as well.
So what is the expected behavior?
Edit 1:
A reference to the appropriate section of the Standard would be helpful, please.
Edit 2:
I checked with VS 2010 and it has objections only with the using declaration X::f2. However it is not about ambiguity of 'X' (as in the case of gcc and Comeau). It is about "error C2876: 'X' : not all overloads are accessible".
Edit 3:
struct X{
void f(){}
};
struct Y : X{
struct trouble{
void f(){}
};
};
struct trouble : X{
};
struct letscheck : Y, trouble{
using trouble::f;
};
int main(){}
Here I have attempted (purposefully) to create an issue with types in using declaration. Gcc still compiles this fine and so does VS2010. Comeau still gives error (as expected) about ambiguous types 'trouble'. Going by explanations given for the initial queries, it appears GCC and VS2010 are wrong. Is that correct?
I don't think that any of these are ill-formed. First, for using X::f2, X is looked up, and this will unambiguously yield the class type X. Then f2 in X is looked up, and this is unambiguous too (it is not looked up in D!).
The second case will work for the same reason.
But if you call f2 on a D object, the call will be be ambiguous because the name f2 is looked up in all subobjects of D of type X, and D has two such subobjects, and f2 is a non-static member function. The same reason holds for the second case. It does not make a difference for this whether you name f3 using Z::X or X directly. Both of these designate the class X.
To get an ambiguity for the using declaration, you need to write it differently. Note that in C++0x using ThisClass::...; is not valid. It is in C++03 though, as long as the whole name refers to a base-class member.
Conversely, if this would be allowed in C++0x, the whole using declaration would also be valid, because C++0x does not take subobjects into account for name-lookup: D::f2 unambiguously refers to only one declaration (the one in X). See DR #39 and the final paper N1626.
struct D : Y, Z{
// ambiguous: f2 is declared in X, and X is a an ambiguous base class
using D::f2;
// still fine (if not referred to by calls/etc) :)
using Z::X::f3;
};
struct E : D {
// ambiguous in C++03
// fine in C++0x (if not referred to by an object-context (such as a call)).
using D::f2;
};
The C++03 Standard describes this in paragraphs 10.2 and 3.4.3.1.
Response for Edit3:
Yes, GCC and VS2010 are wrong. trouble refers to the type found by the injected class name of ::trouble and to the nested class found as Y::trouble. The name trouble preceeding the :: is looked up using unqualified lookup (by 3.4.1/7, which delegates to 10.2 in the first bullet) ignoring any object, function and enumerator names (3.4.3/1 - there are no such names in this case, though). It then violates against 10.2's requirement that:
If the resulting set of declarations are not all from sub-objects of the same type ... the program is ill-formed.
It is possible that VS2010 and GCC interpret C++0x wording differently than Comeau and retroactively implement that wording:
In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.
This means that non-base classes are considered, but it is an error if a non-base class is named. If the Standard would intend to ignore non-base class names, it would say can only here, or spell it out explicitly (both practices are done). The Standard however is not at all consequent with its use of shall and can. And GCC implements C++0x wording, because it rejects otherwise completely fine C++03 code, just because the using declaration contains its class-name.
For an example of the unclear wording, consider the following expression:
a.~A();
This is syntactically ambiguous, because it can be a member function call if a is a class object, but it can be a pseudo-destructor-call (which is a no-op) if a has a scalar type (such as int). But what the Standard says is for the syntax of a pseudo-destructor call and class member access at 5.2.4 and 5.2.5 respectively
The left-hand side of the dot operator shall be of scalar type.
For the first option (dot) the type of the first expression (the object expression ) shall be “class object” (of a complete type).
That is the wrong use, because it does not clear up the ambiguity at all. It should use "can only", and compilers interpret it in that way. This has mostly historical reasons, as some committee-member recently told me on usenet. See The rules for the structure and drafting of International Standards, Annex H.
using X::f2; should not work due to private inheritance of below code
struct Y : private X{
void f4();
};
It is not possible to access members of X through Y.
So X::f2 would conflicts.
Z::X::f2 should work.
Or Z::f2 should work.
First, to clarify Johannes' answer. When you say using Z::X::f2;, the compiler does not "build a path" to f2 to keep track of how it should be accessed. Since Z::X is the same thing as X, the declaration is exactly the same as saying using X::f2;. Contrast it with this example:
struct A { void f() {} void g() {} };
struct B { void f() {} void g() {} };
struct C { typedef A X; };
struct D { typedef B X; };
struct E : A, B {
using C::X::f; // C::X == A
using D::X::g; // D::X == B
};
The syntax Z::X works not because of inheritance or membership, but because the identifier X is accessible from the scope Z. You are even allowed to write Z::Z::Z::Z::X::X::X::X ad nauseam, because every class brings its own name into its own scope. Thus :: here does not express inheritance.
Now, to solve the problem. f2 is inherited by Y and Z from X. Thus, it is a first-class member of Y and Z. E doesn't need to know about X because it is a hidden implementation detail. So, you want
struct D : Y, Z{
using Y::f2; // error: inaccessible
using Z::f3;
};
To explain in terms of 9.1/2 as you ask:
A class-name is inserted into the
scope in which it is declared
immediately after the class-name is
seen. The class-name is also inserted
into the scope of the class itself;
this is known as the
injected-class-name.
The name X is injected into X as X::X. It is then inherited into Y and Z. Y and Z do not implicitly declare X in their own scope.
10.2/2:
The following steps define the result of name lookup in a class
scope, C. First, every declaration for
the name in the class and in each of
its base class sub-objects is
considered. …
If the resulting set of declarations
are not all from sub-objects of
the same type, or the set has a
nonstatic member and includes members
from distinct sub-objects, there is an
ambiguity and the program is
ill-formed. Otherwise that set is the
result of the lookup.
Note that I bolded the plural word sub-objects. Although the name X is found in two sub-objects, they are both the same type, namely X.