According to the odr-use defintion, an object is odr-used if a reference is bound to it. This is why f makes S::x odr-used I believe. What I can not understand is how is that any different from the comma operator which also binds its arguments to reference parameters
struct S {
static const int x = 0; // static data member
// a definition outside of class is required if it is odr-used
};
const int& f(const int& r);
int n = b ? (1, S::x) // S::x is not odr-used here
: f(S::x); // S::x is odr-used here: a definition is required
Those are just examples for how an overloaded comma operator might be defined in a class. It's the usage of such an overload that would necessarily trigger odr-use, when you bind to the arguments.
That usage hasn't been written into your program, nor has an operator overload at all for that matter.
You're just using the built-in comma operator.
(A more interesting question might be whether the right-most operand to this operator is still odr-used, because from the wording it looks to me like it is! Do remember that an odr-use violation isn't required to generate a build error, and in certain situations it won't.).
I would argue that the cppreference page is possibly a little unclear in that regard.
The built-in comma does not bind its operands to anything.
Overloaded commas do, but overloaded operators are just functions with a funny spelling.
Related
Look at this snippet:
struct Foo {
int a, b;
};
extern Foo f;
consteval bool fn() {
return &f.a < &f.b;
}
int main() {
return fn();
}
Suppose that this is the whole program (no other translation units), f has no definition available (it's only extern declared). But fn takes the address of a subobject of f. Of course, the compiler can evaluate &f.a < &f.b without the definition. But I'm not sure what the standard says about this case. Does &f.a < &f.b odr-use f? cppreference says that
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken
I tried to interpret the "Formally" part which is described afterwards (and I also checked the current draft standard about this), but I couldn't draw a confident conclusion.
It would make sense that in this case f is not odr-used (because it's not really needed to evaluate the expression), but the "its address is taken" at cppreference may mean that f is odr-used.
I don't see any exception from the odr-use definition that would apply here.
There is an id-expression naming f and it is potentially-evaluated. So the base requirements of [basic.def.odr]/4 for odr-use are met. There are a few exceptions listed there:
(4.1) is an exception for only reference type variables.
(4.2) applies only to variables that are usable in constant expressions, which doesn't apply here because f is neither a const integral/enumerations type, nor constexpr. See [expr.const].
(4.3) is specific to discarded-value expressions.
None of these exceptions apply, so f is odr-used and consequently a definition must exist.
I think there are multiple places where the odr-use rule is stricter than would intuitively be necessary. I asked a similar question about default member initializers which aren't actually used by any constructor and the answer there also was that a variable/function used in it is odr-used even if there is no way of it ever being evaluated. See Is use in an unused default member initializer still an odr-use?.
struct A{
operator auto(){
return 0;
}
};
int main(){
A a;
a.operator auto(); // #1
a.operator int(); // #2
}
GCC accepts that #2 is the right way to call the conversion function explicitly while Clang accepts #1.
It seems that #1 is ill-formed due to the following rule:
dcl.spec.auto#6
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
This usage a.operator auto() is not explicitly allowed in section [dcl.spec.auto], hence it should be ill-formed. However, for the second usage, which is accepted by GCC, the standard does not say that the conversion-function-id where the conversion-type-id is replaced by the deduced type denotes the name of the conversion function. In other words, the declared conversion-function-id in the declaration is operator auto rather than operator int. The former has the same token as the declarator-id of the declaration. According to the grammar, the unqualified-id operator auto should be the name of that conversion function. So, how to explicitly call this conversion function? Is it underspecified in the standard about which is the name of the conversion function when it contains a placeholder specifier?
It seems, that this is not specified precisely enough.
From 10.1.7.4 The auto specifier:
The placeholder type can appear with a function declarator in the
decl-specifier-seq, type-specifier-seq, conversion-function-id, or
trailing-return-type, in any context where such a declarator is valid.
Reading precisely, one might distinguish here between "can" and the stronger "can only", i.e. potentially opening up room for degrees of freedom for compiler intrinsics (strictly wrong vs. unspecified behavior).
And 3.4.5 class member access says:
7 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.
Again leaving room for interpretation if the auto keyword can effectively be a fully qualified conversion-type-id within this context or not.
Your question itself might have to be further branched, namely
What are the overloading rules for the operator auto() usage in detail, i.e. should it be available for regular candidates competition already on class definition level? (not the case for Clang and Gcc, both accept the operator a priori besides an extra operator int() ...)
Can the operator auto() be called with explicit member operator referring (your case 1), i.e. effectively, has it a (unique) accessible name? Allowing that would be contradictory to all other explicitly allowed use cases for the keyword.
I've seen explicit tests for this within several clang revisions so its behavior is not an artefact of implicit naming convention applicance but an explicitly desired behavior obviously.
As already mentioned within the comments, Clang's behavior is a bit more overall consistent here at least in comparison to gcc since it's totally clear there, where the auto keyword is used for type deduction and where for name / function-id resolution. The operator auto() there is handled as a more explicit own entity, whereas for gcc, it has anonymous character similar to a lambda but is involved within candidates competition even for the explicit member operator access way.
struct A{
operator auto(){
return 0;
}
};
int main(){
A a;
a.operator auto(); // #1
a.operator int(); // #2
}
GCC accepts that #2 is the right way to call the conversion function explicitly while Clang accepts #1.
It seems that #1 is ill-formed due to the following rule:
dcl.spec.auto#6
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
This usage a.operator auto() is not explicitly allowed in section [dcl.spec.auto], hence it should be ill-formed. However, for the second usage, which is accepted by GCC, the standard does not say that the conversion-function-id where the conversion-type-id is replaced by the deduced type denotes the name of the conversion function. In other words, the declared conversion-function-id in the declaration is operator auto rather than operator int. The former has the same token as the declarator-id of the declaration. According to the grammar, the unqualified-id operator auto should be the name of that conversion function. So, how to explicitly call this conversion function? Is it underspecified in the standard about which is the name of the conversion function when it contains a placeholder specifier?
It seems, that this is not specified precisely enough.
From 10.1.7.4 The auto specifier:
The placeholder type can appear with a function declarator in the
decl-specifier-seq, type-specifier-seq, conversion-function-id, or
trailing-return-type, in any context where such a declarator is valid.
Reading precisely, one might distinguish here between "can" and the stronger "can only", i.e. potentially opening up room for degrees of freedom for compiler intrinsics (strictly wrong vs. unspecified behavior).
And 3.4.5 class member access says:
7 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.
Again leaving room for interpretation if the auto keyword can effectively be a fully qualified conversion-type-id within this context or not.
Your question itself might have to be further branched, namely
What are the overloading rules for the operator auto() usage in detail, i.e. should it be available for regular candidates competition already on class definition level? (not the case for Clang and Gcc, both accept the operator a priori besides an extra operator int() ...)
Can the operator auto() be called with explicit member operator referring (your case 1), i.e. effectively, has it a (unique) accessible name? Allowing that would be contradictory to all other explicitly allowed use cases for the keyword.
I've seen explicit tests for this within several clang revisions so its behavior is not an artefact of implicit naming convention applicance but an explicitly desired behavior obviously.
As already mentioned within the comments, Clang's behavior is a bit more overall consistent here at least in comparison to gcc since it's totally clear there, where the auto keyword is used for type deduction and where for name / function-id resolution. The operator auto() there is handled as a more explicit own entity, whereas for gcc, it has anonymous character similar to a lambda but is involved within candidates competition even for the explicit member operator access way.
I got an official answer to this question that decltype should not trigger function compilation. In fact decltype on a function that is declared but not defined is legal.
Next question, should taking the address of a function trigger the compilation of a function? Take this example:
template <typename T>
void foo(T&& x) { x.func(); }
int main()
{
auto bar = &foo<int>;
}
All the compilers I've tested fail with an error like:
Request for member func in x, which is of non-class type int
But if I just define foo and don't declare it, the code compiles fine. Can someone provide me with an official source on whether taking the address of a function should require it's compilation?
3.2/2:
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. ... A non-overloaded
function whose name appears as a potentially-evaluated expression or a
member of a set of candidate functions, if selected by overload
resolution when referred to from a potentially-evaluated expression,
is odr-used, unless it is a pure virtual function and its name is not
explicitly qualified.
Then 3.2/3:
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required. The definition can appear explicitly in the program, it can
be found in the standard or a user-defined library, or (when
appropriate) it is implicitly defined (see 12.1, 12.4 and
12.8). An inline function shall be defined in every translation unit in which it is odr-used.
The function name is definitely not an unevaluated operand (for example to sizeof, decltype), AND it appears in an expression, so it's potentially evaluated. Then the second one requires exactly one non-inline definition, or identical inline definitions in each translation unit.
See this example:
struct Foo
{
int a;
int b;
bool operator == (const Foo & x)
{
return a == x.a && b == x.b;
}
};
int main ()
{
Foo a;
a = {1, 2};
if (a == {1, 2}) // error: expected primary-expression before ‘{’ token
{
}
}
The line a={1,2} is fine. The braces are convert to a Foo to match the argument type of the implicit operator= method. It still works if operator= is user-defined.
The line if (a=={1,2}}) errors as indicated.
Why does the expression {1,2} not convert to a Foo to match the user-defined operator== method?
List-initialization cannot be used as an argument to an operator in the general case. Per Paragraph 8.5.4/1 of the C++11 Standard:
[...] List-initialization can be used
— as the initializer in a variable definition (8.5)
— as the initializer in a new expression (5.3.4)
— in a return statement (6.6.3)
— as a for-range-initializer (6.5)
— as a function argument (5.2.2)
— as a subscript (5.2.1)
— as an argument to a constructor invocation (8.5, 5.2.3)
— as an initializer for a non-static data member (9.2)
— in a mem-initializer (12.6.2)
— on the right-hand side of an assignment (5.17)
The last item explains why list-initialization is allowed on the right side of operator =, even though it is not allowed in general for an arbitrary operator.
Because of the fifth item above, however, it can be used as an argument to a regular function call, this way:
if (a.operator == ({1, 2}))
It's just simply not supported.
Initializer lists are explicitly defined to be valid in initializations ([C++11: 8.5.4]), and assignments:
[C++11: 5.17/9]: A braced-init-list may appear on the right-hand side of
an assignment to a scalar, in which case the initializer list shall have at most a single element. The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T(v) except that no narrowing conversion (8.5.4) is allowed. The meaning of x={} is x=T().
an assignment defined by a user-defined assignment operator, in which case the initializer list is passed as the argument to the operator function.
There is no standard wording to allow other, arbitrary cases.
If it were allowed, in this example, the type of {1,2} would be fairly ambiguous. It would be a complicated language feature to implement.
An explicit cast is required.
if (a == (Foo){1, 2})
{
}
When you are using the expression containing the user defined type a and == operator
Overloaded operator function gets called which as per definition you have given requires an argument of a reference of an object of class Foo
so your expression should be like a==b
where b is an object of class Foo
Here with this expression you will be able to compare data members of b and a and hence know if they are equal or not
No real reason.
C++ is the result of a committee effort so sometimes strange but deliberate decisions may slip through because of complex political/sociological dynamics.
C++ syntax is hard. Very hard. Almost unbelievably hard. There are rules even go like "if you can parse this arbitrarily long sequence of tokens as both this or that, then it's this".
It took many years for compilers even to simply agree on what is C++ and what is not.
In this case my wild guess is that they didn't like the idea that cases that looked very similar:
MyClass x = {...};
MyClass y; y = {...};
would be handled differently so there is a special provision for assignment to allow the syntax.
From a technical point of view I don't see what are the problems of allowing it for other operators too, and on the other hand if there are problems (e.g. for overloading, template instantiation etc.) I don't see how assignment can hope to escape them.
EDIT
g++ allows using not only strict operator= but also operator+=, operator-= and similar "augmented assignment". May be the logical problems happens only if you allow non-member overloads (that are forbidden for assignment and augmented assignment operators).