void f(int x) {
int (x), 1;
}
Clang compiles it, GCC doesn't. Which compiler is correct?
IMO, the wording in [stmt.ambig] is clear enough on this:
An expression-statement with a function-style explicit type conversion 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.
[Note: If the statement cannot syntactically be a declaration, there is no ambiguity, so this rule does not apply. The whole statement might need to be examined to determine whether this is the case.
The wording speaks of an entire (expression-)statement.
Your statement cannot be parsed as a declaration, because the lexeme 1 is grammatically not a declarator. There is no ambiguity: it might look ambiguous if we looked solely at int(x), but the standard quite explicitly denies that if some prefix of the statement parses as a declaration, the entire statement is considered a potential declaration.
In fact, core experts had a highly similar discussion back in 2002 over core issue 340---I highlighted the important bits. Here, again, we have a supposed declaration that contains an incompatible sub-construct.
Consider the following program:
struct Point {
Point(int){}
};
struct Lattice {
Lattice(Point, Point, int){}
};
int main(void) {
int a, b;
Lattice latt(Point(a), Point(b), 3); /* Line X */
}
The problem concerns the line marked /* Line X */, which is an ambiguous
declarations for either an object or a function. The clause that
governs this ambiguity is 8.2 [dcl.ambig.res] paragraph 1, and reads:
The ambiguity arising from the similarity between a function-style
cast and a declaration mentioned in 6.8 [stmt.ambig] [..]
Based on this clause there are two
possible interpretations of the declaration in line X:
The declaration of latt declares a function with a return value of the
type Lattice and taking three arguments. The type of the first two
arguments is Point and each of these arguments is followed by a
parameter name in redundant parentheses. The type of the third
argument can not be determined, because it is a literal. This will
result in a syntax error.
The declaration of latt declares an object,
because the other option (a function declaration) would result in a
syntax error. Note that the last sentence before the "[Note:" is not
much help, because both options are declarations.
Steve Adamczyk: a number of people replied to this posting on
comp.std.c++ saying that they did not see a problem.
The original
poster replied:
I can't do anything but agree with your argumentation. So there is
only one correct interpretation of clause 8.2 [dcl.ambig.res]
paragraph 1, but I have to say that with some rewording, the clause
can be made a lot clearer, like stating explicitly that the entire
declaration must be taken into account and that function declarations
are preferred over object declarations.
I would like to suggest the following as replacement for the current
clause 8.2 [dcl.ambig.res] paragraph 1:
The ambiguity arising from the similarity between a functionstyle cast
and a declaration mentioned in 6.8 [stmt.ambig] […]
The working group felt that the current wording is clear enough.
Related
Recent versions of clang (since clang-11) issue a warning when compiled with -pedantic for the following segment of code:
namespace foo {
template <int A>
struct Bar {
~Bar();
};
} // namespace foo
template <int A>
foo::Bar<A>::~Bar(){}
With the generated warning (and error with -Werror) being:
<source>:10:12: error: ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~' [-Werror,-Wdtor-name]
foo::Bar<A>::~Bar(){}
~~~~~~~~~~~^~
::Bar
Live Example
clang is the first compiler I've seen to issue this diagnostic, and as far as I'm aware, what was written above is completely valid C++. Two different ways that seem to suppress this is to either define within the same namespace or explicitly qualify the destructor name -- such as:
...
template <int A>
foo::Bar<A>::~Bar<A>(){}
Is Clang's diagnostic here correct? It's my understanding that the type's name would absolutely be in the correct name-scope as foo::Bar<A>::~ -- and that the qualification of ~Bar<A>() should be unnecessary.
From what I have learned since posting the question, this warning is, strictly-speaking, correct -- though it most likely a defect in the wording of the standard.
According to Richard Smith in LLVM Bug 46979:
The diagnostic is correct, per the standard rules as written; strictly-speaking, the C++ rules require this destructor to be written as
template<typename T>
A::B<T>::~B<T>()
Per C++ [basic.lookup.qual]p6:
In a qualified-id of the form:
nested-name-specifier[opt] type-name :: ~ type-name
the second type-name is looked up in the same scope as the first.
This means that the second B is looked up in class A, which finds only the class template B and not the injected-class-name.
This is not an especially useful rule, and likely isn't the intended rule, which is why this diagnostic (along with a bunch of similar diagnostics for plausible but formally incorrect destructor names) is disabled by default but included in -pedantic.
Looking further into this, I can find the mentioned passage for [basic.lookup.qual]/6 in the C++20 standard, but it appears drafts for C++23 have changed this -- which points towards this most likely being a defect.
In drafts for [basic.lookup.qual] for C++23, this whole section has been overhauled and is currently, at the time of writing, replaced with [basic.lookup.qual]/4 which states:
If a qualified name Q follows a ~:
(4.1) If Q is a member-qualified name, it undergoes unqualified lookup as well as qualified lookup.
(4.2) Otherwise, its nested-name-specifier N shall nominate a type.
If N has another nested-name-specifier S, Q is looked up as if its lookup context were that nominated by S.
(4.3) Otherwise, if the terminal name of N is a member-qualified name M, Q is looked up as if ~Q appeared in place of M (as above).
(4.4) Otherwise, Q undergoes unqualified lookup.
(4.5) Each lookup for Q considers only types (if Q is not followed by a <) and templates whose specializations are types.
If it finds nothing or is ambiguous, it is discarded.
(4.6) The type-name that is or contains Q shall refer to its (original) lookup context (ignoring cv-qualification) under the interpretation established by at least one (successful) lookup performed.
[Example 4:
struct C {
typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
void f() {
p->C::I::~I(); // I is looked up in the scope of C
q->I1::~I2(); // I2 is found by unqualified lookup
}
struct A {
~A();
};
typedef A AB;
int main() {
AB* p;
p->AB::~AB(); // explicitly calls the destructor for A
}
— end example]
(The full quote has been posted here since this is from a draft, and the wording may be subject to change in the future)
This appears to explicitly correct this issue by ensuring that lookup is performed as one would expect.
So basically the diagnostic is correct under older versions of C++ due to a defect in the wording -- but C++23 appears to change that. I am not sure whether this will be retroactively fixed in older versions as a defect given that it appears no compiler actually follows this pedantic requirement.
A code snippet from cppreference.com is like this:
struct M { };
struct L { L(M&); };
M n;
void f() {
M(m); // declaration, equivalent to M m;
L(n); // ill-formed declaration
L(l)(m); // still a declaration
}
L(n); is commented with "ill-formed declaration".
But nearly all compilers issue message like this: no default constructor exists for class "L". That is to say, it's not considered to be ill-formed, right? Because if i throw a line L() = default; into L's body, it compiles successfully.
Is the comment wrong or misleading or compilers are not strictly standard-conforming?
Follow Up
Seems that i made something wrong with ill-formed:
ill-formed - the program has syntax errors or diagnosable semantic
errors. A conforming C++ compiler is required to issue a diagnostic, > even if it defines a language extension that assigns meaning to such > code (such as with variable-length arrays). The text of the standard > uses shall, shall not, and ill-formed to indicate these requirements.
In light of that, that line is semantically wrong.
Thanks, for you guys' answers and comments.
I think that the example demonstrates that declarator may be enclosed in parentheses.
So this declaration
M(m);
is equivalent to
M m;
that is there is declared an object m of the type M.
However this record
L(n);
can be considered as an expression statement with calling the constructor L( M & ) with the argument n of the type M or as a declaration.
The C++ Standard resolves such an ambiguity as a declaration instead of the expression statement. So in this record n is the name of the created object. But the class L does not have the default constructor. So the declaration is ill-formed because the structure L does not have the default constructor that is required for this declaration.
From the C++ 14 Standard (6.8 Ambiguity resolution)
1 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.
To make an expression statement instead of the declaration you can write for example
( L )( n );
This record
L(l)(m);
is a correct declaration. There is declared the object l of the type L using the constructor L( M & ).
I use the following in my C++ code:
int a = 0, b = a;
I would like to know if this behaviour is reliable and well defined (left to right order of name declaration) and that my code will not break with other compilers with an undeclared name error.
If not reliable, I would break the statement:
int a = 0;
int b = a;
Thank you.
I believe the answer is no.
It is subject to core active issue 1342 which says:
It is not clear what, if anything, in the existing specification requires that the initialization of multiple init-declarators within a single declaration be performed in declaration order.
We have non-normative note in [dcl.decl]p3 which says:
...[ Note: A declaration with several declarators is usually
equivalent to the corresponding sequence of declarations each with a
single declarator. That is
T D1, D2, ... Dn;
is usually equivalent to
T D1; T D2; ... T Dn;
...
but it is non-normative and it does not cover the initialization case at all and as far as I can tell no normative wording says the same thing.
Although the standard does cover the scope of names in [basic.scope.pdecl]p1 which says:
The point of declaration for a name is immediately after its complete
declarator and before its initializer (if any), except as noted below.
[ Example:
unsigned char x = 12;
{ unsigned char x = x; }
Here the second x is initialized with its own (indeterminate) value.
— end example ]
The fact that you thought to ask this question suggests that the style is not great. Even though the one-line version is almost guaranteed† to work, I would still go with the two-line approach for the greater clarity to human readers.
† I initially said it was guaranteed, but I will step back from that. After reviewing the relevant portion of the spec, I can see how language lawyers would complain that this guarantee is not explicitly stated. (As Shafik Yaghmour points out, core active issue 1342 notes the lack of an explicit guarantee, albeit with phrasing that suggests that such a guarantee should be present.)
I will step back only to "almost guaranteed", though, as it is strongly implied by "Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.". That is, the analysis of int a = 0, b = a; has two parts: one where a variable named a is initialized to 0, and one where a variable named b is initialized to the value of a. If you are truly keeping these parts separate, then the first part would have to finish before the second part begins (otherwise they are not as if each was in a declaration by itself), so a would have the value 0 before b is initialized. I accept that this might be not definite enough for the language lawyers, but it should be good enough for a compiler's bug report if there is a compiler for which that line does not work as intended.
My apologies for not looking up the spec earlier. The "language-lawyer" tag was not present when I initially answered.
A declaration statement that defines multiple variables separated by comma is exactly equivalent to multiple declaration statements that defines a single variable in the same order because the scope of a variable begins just after it's name, but there are (at least) two exceptions:
1) When a variable declaration hides a type with the same name, as in:
struct S {};
S S, T;
Is different from
struct S {};
S S;
S T; //error: S is a variable name
But
struct S {};
S S, T{S};
Is equivalent to
struct S{};
S S;
struct S T{S};
2) When using the auto and decltype(auto) specifiers:
auto i{0}, j{i}, k{2.0}; // error: deduction for auto fails, double or int?
Is different from
auto i{0};
auto j{i};
auto k{2.0};
In any case, evaluation order is always from left to right.
I'm a little bit supprised as i see this compilation error;
Here is the example:
class A
{
public:
enum eA { eA1, eA2 };
class B
{
public:
B(eA e, int i = 0);
};
A(const B &b);
};
A::A(const B &b)
{
}
A::B::B(eA e, int i /*= 0*/)
{
}
int main()
{
A::B b(A::eA1); // OK
A a0(b); // OK
A a1(A::B(A::eA1, 0)); // OK
A a2(A::B(A::eA2)); //error C2751: 'A::eA2': the name of a function parameter cannot be qualified
return 0;
}
As pointed in comments, A a2(A::B(A::eA2)); doesn't compile. Why?
I'm not asking how to compile it. Why it doesn't compile?
It compiles, if first parameter type of class-B is not from class-A. for example with int it compiles.
This is a Most Vexing Parse issue. The canonical case would be:
T t( U(x) );
where T and U are previously known to be type names. This could be parsed in two valid ways:
a declaration of t as an object of type T, with the initializer being the expression U(x), a function-style cast of the variable x to a temporary U
a declaration of t as a function returning T, with 1 parameter of type U and name x; and there are redundant parentheses around x. (Declarators may be parenthesized).
The text in the Standard for disambiguating is in [dcl.ambig.res]/1. What it says is that parsing this code hinges on whether U(x) would be a declaration or an expression, and then refers to [stmt.ambig].
In [stmt.ambig]/2 there is a clarifying example. I won't reproduce the full example here (you can look it up in a standards draft) but the accompanying text is:
If the statement cannot syntactically be a declaration, there is no ambiguity
[...] This is of course ill-formed for semantic reasons, but that does not affect the syntactic analysis. In those cases the statement is a declaration.
What this is trying to say is that if the code can be matched to the rules in the language grammar for a declaration, then it is a declaration, even if the code subsequently falls foul of a semantic rule.
Looking at your variation now, where the inner code is (simplified) U(A::e) where e is an enumerator in the scope of A.
I believe this code does still match the grammar rule for a declaration. See [dcl.decl]/4 grammar specification (selected parts):
noptr-declarator: declarator-id attribute-specifier-seqopt
declarator-id: ...opt id-expression
and id-expression can be qualified-id or unqualified-id, and finally, A::e is a qualified-id.
Even though there is a semantic rule [dcl.meaning]/1 that the declarator-id can only be a qualified-id in certain contexts, excluding this case, that's not a grammar rule.
So I would say VC is correct to reject the code.
To fix the code, assuming the intent is for a2 to be an object declaration, you can use the same techniques as mentioned on the canonical MVP thread:
A a2{A::B(A::eA2)}; // braced initialization
A a2((A::B(A::eA2))); // function declarations can't have extra parentheses around the entire parameter declaration, so this cannot be a function declaration
Consider the following program:
extern int x;
auto x = 42;
int main() { }
Clang 3.5 accepts it (live demo), GCC 4.9 and VS2013 do not (live demo for the former). Who is right, and where is the correct behavior specified in the C++ Standard?
There's surprisingly little in the standard about this. About all we hear about redeclaration is:
[C++11: 3.1/1]: A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. [..]
and the only relevant part of auto's semantics:
[C++11: 7.1.6.4/3]: Otherwise, the type of the variable is deduced from its initializer. [..]
(reminding us that the type of x is int).
We know that a variable must be given the same type by all declarations:
[C++11: 3.5/10]: After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic.
and the "after all adjustments of types" ought to take care of any questions regarding auto's participation in all of this; my interpretation, then, is that this is inherently a valid redeclaration (and definition) of the x at global scope with type int, and that Clang is correct. Even if we propose that auto does not count as "adjustment of type", since no diagnostic is required, at worst all listed implementations are compliant in their own way.
I believe GCC and Visual Studio are taking the following as inspiration:
[C++11: 7.1.6.4/5]: A program that uses auto in a context not explicitly allowed in this section is ill-formed.
…but I think that this is short-sighted. It seems unlikely that the standard language is intended to prohibit the usual redeclaration rules, just because they are not repeated or explicitly referenced from within 7.1.6.4.
C++14 adds wording that relates to declarations of functions with deduced types:
[C++14: 7.1.6.4/13]: Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [..]
By symmetry one might suggest that, in your int case, it is intended that GCC and VS be correct in rejecting the program. However, this is a different feature (since deduction cannot be applied to mere declarations) and thus a different scenario.
Either way, improved standard wording would help here. I consider it a [reasonably minor] editorial defect.
Note
I answered a question that was closed a duplicate of this one. I asked for merge and was told instead to provide an answer here. See below for my original answer.
Update clang is correct
I asked this question on twitter and the response I received from Richard Smith was as follows:
Not a defect, it's intentional that this restriction applies only to deduced return types and not to variable types. For variables, it's just a convenience shorthand, but return type deduction affects something more fundamental about functions (and especially function templates).
So the logic is that this is allowed by [dcl.spec.auto] and to restrict this for deduced return types paragraph [dcl.spec.auto]p11 was added to the section. Otherwise there is no restriction and therefore this is not restricted for the variables case.
Original
Currently [dcl.spec.auto] does not seem to cover this case explictly but it does say in [dcl.spec.auto]p5:
A program that uses auto or decltype(auto) in a context not explicitly allowed in this subclause is ill-formed.
and we can see it makes a similar case for functions ill-formed in [dcl.spec.auto]p11:
Redeclarations or specializations of a function or function template
with a declared return type that uses a placeholder type shall also
use that placeholder, not a deduced type. Similarly, redeclarations or
specializations of a function or function template with a declared
return type that does not use a placeholder type shall not use a
placeholder. [ Example:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
....
So although this could use clarification as currently worded it feels like gcc is correct and this is ill-formed.
I'd imagine the restriction in [dcl.spec.auto]p11 exists because otherwise, that would allow:
int f();
auto f(); // What's the return type here?
The thing is, you can have an undeduced type type has the return type of a function. There are no deduction rules based on previous declarations, which is why such mixing is disallowed for functions, even though the following would be perfectly fine:
int f();
auto f() { return 1; }
This problem does not exist for variables:
extern int v;
extern auto v; // ill-formed
Any declaration-only variables has to use a non-placeholder type. What this means is that if you use a placeholder type for the definition of v, it can get deduced without any problems and then of course has to match the non-placeholder type used in the first declaration.
extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.