I'm just curious to know why this small piece of code compiles correctly (and without warnings) in Visual Studio. Maybe the result is the same with GCC and Clang, but unfortunately I can't test them now.
struct T {
int t;
T() : t(0) {}
};
int main() {
T(i_do_not_exist);
return 0;
}
T(i_do_not_exist); is an object declaration with the same meaning as T i_do_not_exist;.
N4567 § 6.8[stmt.ambig]p1
There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
§ 8.3[dcl.meaning]p6
In a declaration T D where D has the form
( D1 )
the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration
T D1
Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.
Because it defines a variable of type T:
http://coliru.stacked-crooked.com/a/d420870b1a6490d7
#include <iostream>
struct T {
int t;
T() : t(0) {}
};
int main() {
T(i_do_not_exist);
i_do_not_exist.t = 120;
std::cout << i_do_not_exist.t;
return 0;
}
The above example looks silly, but this syntax is allowed for a reason.
A better example is:
int func1();
namespace A
{
void func1(int);
struct X {
friend int (::func1)();
};
}
Probably other examples could be found.
Related
I have some code which requires me to use *this, but I want it to be noexcept friendly:
struct foo;
// Would actually be something with conditional noexcept
void do_something(foo&);
struct foo {
void fn()
noexcept(noexcept(::do_something(*this)))
{
::do_something(*this);
}
};
However, gcc rejects this:
<source>:7:43: error: invalid use of 'this' at top level
noexcept(noexcept(::do_something(*this)))
If I just access a member, gcc is fine:
void do_something(int);
struct bar {
int x;
void fn()
noexcept(noexcept(::do_something(x)))
{
::do_something(x);
}
};
However, if I access the member through the this pointer, gcc complains again:
struct baz {
int x;
void fn()
noexcept(noexcept(::do_something(this->x)))
{
::do_something(this->x);
}
};
Diagnostic:
<source>:7:42: error: invalid use of 'this' at top level
noexcept(noexcept(::do_something(this->x)))
Every other compiler I tried accepts using this inside the noexcept specification, but I don't actually know if it's gcc that has the bug or all the other compilers.
Can the keyword this be used inside a noexcept specification?
Yes, it is allowed. [expr.prim.this]p2 says:
If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifier-seq and the end of the function-definition, [...].
The cv-qualifier-seq refers to the cv qualifiers of the member function, which appear before the noexcept specifier:
parameters-and-qualifiers:
( parameter-declaration-clause ) cv-qualifier-seq[opt] ref-qualifier[opt]
noexcept-specifier[opt] attribute-specifier-seq[opt]
So, this is a valid expression to use in the noexcept-specifier. This was a DR (cwg1207), which gcc doesn't implement. The bug report.
The following doesn't compile in clang:
#include <iostream>
void f() { std::cout << "f()\n"; }
struct S {
typedef void(*p)();
operator p() { return f; }
};
int main()
{
S s;
s.operator p()();
}
Yields:
main.cpp:13:16: error: unknown type name 'p'; did you mean 'S::p'?
s.operator p()();
^
S::p
main.cpp:6:19: note: 'S::p' declared here
typedef void(*p)();
^
But it should, as the expression s.operator p()() accesses a public member function of the object S::s. Am I missing something?
If I'm wrong, I would appreciate a quote from the Standard supporting the answer.
This appears to be a bug in Clang. I believe the code is correct.
Clang 4.0.0 reports:
<source>:13:16: error: unknown type name 'p'; did you mean 'S::p'?
s.operator p()();
^
However, from C++14 3.4.5/7 [basic.lookup.classref]
If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the
object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. In each of these lookups, only names that denote types or templates whose specializations
are types are considered.
[ Example:
struct A { };
namespace N {
struct A {
void g() { }
template <class T> operator T();
};
}
int main() {
N::A a;
a.operator A();
// calls N::A::operator N::A
}
— end example ]
In your example, the type p should have been found in the class without requiring qualification.
Consider the following code:
struct S {
using T = int;
operator T() { return 42; }
};
int main() {
S s;
S::T t = s;
// Is the following line correct?
t = s.operator T();
}
It compiles with GCC (4.9/5.1/6.1), but it fails to compile with clang (3.8/3.7).
The error returned is:
error: unknown type name 'T'; did you mean 'S::T'?
Which compiler is right in this case and why?
Note
Solving it is a matter of qualifying T:
t = s.operator S::T();
The question is not about how to make it work.
I believe this is clang bug (submitted as #27807)
From [basic.lookup.classref]:
If the id-expression is a conversion-function-id, its conversion-type-id is first looked up in the class of the
object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire
postfix-expression. In each of these lookups, only names that denote types or templates whose specializations
are types are considered. [ Example:
struct A { };
namespace N {
struct A {
void g() { }
template <class T> operator T();
};
}
int main() {
N::A a;
a.operator A(); // calls N::A::operator N::A
}
—end example ]
In t = s.operator T();, T is first looked up in the class of S, which should find your typedef and hence end up calling operator int().
I need an explanation on this example from standard [basic.lookup.unqual]/3:
typedef int f;
namespace N {
struct A {
friend void f(A &);
operator int();
void g(A a) {
int i = f(a); // f is the typedef, not the friend
// function: equivalent to int(a)
}
};
}
I would argue that void N::f(A &) is closer than int(a) because it does not involve the type-cast operator. I cannot however be sure because the standard contains only one instance of "type cast".
By the way, compiling that code fails in MSVC2015 (but it works in clang and g++).
Error C2440 'initializing': cannot convert from 'void' to 'int'
UPDATE
Addressing some of the comments.
Type casting is formally known as "type conversions" and they are covered in (12.3) (thanks jefffrey).
What I am looking for is a description of the syntax parsing. In particular, why postfix-expression ( expression-list opt ) is trampled over by simple-type-specifier ( expression-list opt ). Since according to (5.2), both of those expression are evaluated left-to-right. Hence, out of the two candidates to be before the (a), ::N::f should be closer than ::f when evaluating expressions in ::N::A::g.
"Type casting" has nothing to do with this scenario. The rules for argument-dependent lookup include, from [basic.lookup.argdep]:
Let X be the lookup set produced by unqualified lookup (3.4.1) and let Y be the lookup set produced by
argument dependent lookup (defined as follows). If X contains
(3.1) — a declaration of a class member, or
(3.2) — a block-scope function declaration that is not a using-declaration, or
(3.3) — a declaration that is neither a function or a function template
then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. The set of declarations found by the lookup of the name is the union of
X and Y.
The lookup set produced by unqualified lookup for f is:
typedef int f;
That declaration is neither a function nor a function template, therefore Y is empty. We do not consider the friend function f, since it is not visible to unqualified lookup.
There are a few things going on here. As the note containing the example says (3.4/3):
For purposes of determining (during parsing) whether an expression is a postfix-expression for a function call, the usual name lookup rules apply. The rules in 3.4.2 have no effect on the syntactic interpretation of an expression.
So first we need to know whether f is a simple-type-specifier or a postfix-expression, using name lookup rules that don't include section 3.4.2. And under these rules, the function N::f is not visible.
7.3.1.2/3:
If a friend declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3).
Therefore the unqualified lookup does not see any declaration of N::f at all, and finds only ::f, which is a typename. So the syntax is simple-type-specifier ( expression-list opt ) and not postfix-expression ( expression-list opt ), and argument-dependent lookup (3.4.2) does not apply.
( If the unqualified lookup had found a function name, 3.4.2 would apply and would be able to include N::f in the candidate list despite the lack of declaration. If N::f had a previous declaration other than the friend declaration, it would have won the unqualified lookup. )
The statements around the quote are actually quite clear on why the example does not call the function f:
Because the expression is not a function call, the argument-dependent name lookup (3.4.2) does not apply and the friend function f is not found.
The actual question is why the argument-dependent look-up is not applies. It seems 5.2.3 [expr.type.conv] paragraph 1 applies:
A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list.
Clearly, f is a simple-type-specifier: the typedef int f; arranges for that to be the case. Remove this typedef and f isn't a simple-type-specifier anymore resulting in N::A::f being found.
According to [class.conv]/1 (emphasis mine):
Type conversions of class objects can be specified by constructors and by conversion functions.
Thus, both ::N::f and ::N::A::operator int() are functions. However, [namespace.memdef]/3 specifies (emphasis mine):
If a friend declaration in a non-local class first declares a class, function, class template or function template 97 the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3).
Since ::N::f is out of the picture while looking up for names, the closest is ::f which then becomes ::N::A::operator int().
The following code should clarify things. Because it fails to compile (in clang), it shows that ::N::f has preference over ::f when ::N::f is actually in the list of available names.
#include <boost/type_index.hpp>
#include <iostream>
typedef int f;
namespace N {
struct A;
void f( A & )
{
std::cout << "::N::f\n";
}
struct A {
friend void f(A &);
operator int() { std::cout << "Yes, I am a function\n"; return 5; }
void g(A a) {
int i = f(a); // f is the typedef, not the friend
// function: equivalent to int(a)
std::cout << boost::typeindex::type_id_with_cvr<decltype(f(a))>().pretty_name() << '\n';
std::cout << i;
}
};
}
int main()
{
N::A a;
a.g( a );
}
Further explanation
The part
void f( A & )
{
std::cout << "::N::f\n";
}
is both the declaration (and the definition) of ::N::f. That is, it introduces the name ::N::f in the list of names contained in ::N. When evaluating the f in int i = f(a), we look in the list available names and we find ::N::f before ::f. Hence f(a) is type void and the code above fails to compile with the message "cannot initialize a type int with a type void". That is, int i = f(a) calls ::N::f when ::N::f is available even in the presence of ::N::A::operator int.
On the other hand, if we remove the ::N::f definition part, the name ::N::f exists only as introduced by the friend declaration. Since such a name cannot be the result of a lookup, then the f in f(a) is the next available, which is the global typedef ::f. Now that we know that the f in f(a) is int, we can call the function ::N::A::operator int.
Standard (n4296) says as 11.3 §1 (Member access control / Friends) A class specifies its friends, if any, by way of friend declarations. Such declarations give
special access rights to the friends, but they do not make the nominated friends members of the befriending
class.
And at 7.3.1.2 (Namespace member definitions) §3 The friend declaration does not by
itself make the name visible to unqualified lookup or qualified lookup
.
I slightly modified your example to make it simpler to see what actually happens:
any try to declare f outside N (meaning at top level scope) gives an error redefinition of 'f' as different kind of symbol, be it before of after the namespace N block
typedef int f;
namespace N {
struct A {
friend int f(A &);
operator int();
int g(A a) {
int i = f(a); // f is the typedef, not the friend
// function: equivalent to int(a)
return i;
}
int i;
A(int i): i(i) {};
};
}
int f(N::A& a) { // Error here
return 2*a.i;
}
N::A::operator int() {
return this->i;
}
if f is declared (in namespace N) after int i = f(a) f is taken as the int conversion:
#include <iostream>
typedef int f;
namespace N {
struct A {
friend int f(A &);
operator int();
int g(A a) {
int i = f(a); // f is the typedef, not the friend
// function: equivalent to int(a)
return i;
}
int i;
A(int i): i(i) {};
};
int f(A& a) {
return 2*a.i;
}
}
N::A::operator int() {
return this->i;
}
int main() {
N::A a(2);
std::cout << a.g(a) << std::endl;
return 0;
}
outputs 2
if f is declared before int i = f(a) the function declaration inside the namespace has precedence over int conversion:
#include <iostream>
typedef int f;
namespace N {
int f(struct A&); // <= simple DECLARATION here
struct A {
friend int f(A &);
operator int();
int g(A a) {
int i = f(a); // f is the typedef, not the friend
// function: equivalent to int(a)
return i;
}
int i;
A(int i): i(i) {};
};
int f(A& a) {
return 2*a.i;
}
}
N::A::operator int() {
return this->i;
}
int main() {
N::A a(2);
std::cout << a.g(a) << std::endl;
return 0;
}
outputs 4
TL/DR : The example on the standard assumes that there is no declaration of the function f before int i = f(a);. As a friend declaration in a class or in a namespace does not make the name visible to unqualified lookup or qualified lookup, the only declaration that is visible at that time is typedef int f;. So f is taken as the typedef.
But if there is a declaration for the function f in the namespace, it will take precedence over the typedef, because in N scope it hides the top level declaration.
clang doesn't compile the third call to typeid below (see live example). But I can't see anything in §5.2.8 that disallows this, specially when we consider that the expression B::f is not a glvalue of polymorphic class type (see paragraph 3). Also, according to this paragraph the expression B::f is an unevaluated operand, and as such, the call typeid(B::f) should compile. Note that GCC doesn't compile any of the calls to typeid below:
#include <iostream>
#include <typeinfo>
struct A{ int i; };
struct B{ int i; void f(); };
int main()
{
std::cout << typeid(A::i).name() << '\n';
std::cout << typeid(B::i).name() << '\n';
std::cout << typeid(B::f).name() << '\n';
}
As far as I can tell clang is correct, using a non static member is only valid in an unevaluated context if it is a data member. So it looks like gcc is incorrect for the first two cases, but gcc works correctly in the case of sizeof and decltype which also have unevaluated operands.
From the draft C++11 standard section 5.1.1 [expr.prim.general]:
An id-expression that denotes a non-static data member or non-static
member function of a class can only be used:
and includes the following bullet:
if that id-expression denotes a non-static data member and it appears
in an unevaluated operand. [ Example:
struct S {
int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK
—end example ]
The rest of the bullets do not apply, they are as follows:
as part of a class member access (5.2.5) in which the object expression refers to the member’s class61 or a class derived from that
class, or
to form a pointer to member (5.3.1), or
in a mem-initializer for a constructor for that class or for a class derived from that class (12.6.2), or
in a brace-or-equal-initializer for a non-static data member of that class or of a class derived from that class (12.6.2), or
We know that operand is unevaluated from section 5.2.8 which says:
When typeid is applied to an expression other than a glvalue of a
polymorphic class type, [...] The expression is an unevaluated operand
(Clause 5).
We can see from the grammar that an id-expression is either an unqualified-id or a qualified-id:
id-expression:
unqualified-id
qualified-id
Update
Filed a gcc bug report: typeid does not allow an id-expression that denotes a non-static data member.
typeid(A::i).name() doesn't quite do what I thought it would do. I expected it to be a pointer-to-member, but it's actually just an int.
To see this, run this code:
#include <iostream>
struct A{ int i; };
struct B{ int i; void f(void); };
template<typename T>
void what_is_my_type() {
std:: cout << __PRETTY_FUNCTION__ << std:: endl;
}
int main()
{
what_is_my_type<decltype(&A::i)>(); // "void what_is_my_type() [T = int A::*]"
what_is_my_type<decltype(&B::i)>(); // "void what_is_my_type() [T = int B::*]"
what_is_my_type<decltype(&B::f)>(); // "void what_is_my_type() [T = void (B::*)()]"
what_is_my_type<decltype(A::i)>(); // "void what_is_my_type() [T = int]"
what_is_my_type<decltype(B::i)>(); // "void what_is_my_type() [T = int]"
// what_is_my_type<decltype(B::f)>(); // doesn't compile
}
I've put the output in a comment after each call.
The first three calls work as expected - all three work and the type information includes the type of the struct (A or B) as well as the type of member.
The last three are different though. The final one doesn't even compile, and the first two simply print int. I think this is a clue as to what is wrong. It is possible, given a particular A or B, to take the address of that particular member:
A a;
int * x = &(a.i);
*x = 32;
but it is not possible (or even meaningful?) to do this:
B b;
??? y = &(a.f); // what does this even mean?
Finally, to emphasize that this is not about pointers, consider this:
A a;
B b;
int x = a.i;
int y = b.i;
??? z = b.f; // what would this mean? What's its type?