Templates and ODR - c++

The question is about the C++ documentation and standard documentation. Is in the following code the variable x odr-used?
extern int x;
template<class T> T f() { return x; }
It seems to me it is not used, bud where it is stated in documents? (there is the statement, that appearance x in expression is odr-use, but...)

It is odr-used. [basic.def.odr]/2:
An expression is potentially evaluated unless it is an unevaluated operand or a subexpression thereof.
Thus the expression x is potentially evaluated.
[basic.def.odr]/4:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion ([conv.lval]) to x yields a constant expression ([expr.const]) [...]
We can stop here. Applying the lvalue-to-rvalue conversion to x does not yield a constant expression.
This violation of the ODR does not require a diagnostic ([basic.def.odr]/10):
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program outside of a
discarded statement; no diagnostic required.
Even assuming for the sake of argument that f does not odr-use x until/unless a specialization is generated, it still won't help the program, because the program is also ill-formed NDR if "no valid specialization can be generated for a template" ([temp.res]/8), and since every valid specialization of f undoubtedly odr-uses x, no valid specialization can be generated if a definition of x is not present in the program.

Related

Does a consteval function odr-use a global variable if it takes its address?

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?.

Is a function definition required to be instantiated when there is no need to check for narrowing?

Consider the following program:
template<typename T>
constexpr int f()
{
T{}.i; // error if instantiated with [T = double]
return 42;
}
constexpr void g(char);
using U = decltype( g( {f<double>()} ) );
To my understanding, the last line is an error because the call to f<double>() is within a brace initializer, and even though f<T> returns an int, the value of the returned int is needed to decide if it can be narrowed to a char as expected by g. This requires the definition of f to be instantiated with double, which causes an error. Both gcc and clang reject this code.
However, if the definition of g is changed to accept an int parameter:
constexpr void g(int);
then it seems that there is no need to instantiate the definition of f, since the narrowing conversion must succeed. Indeed, gcc accepts this, but clang still instantiates f with double and rejects the code. Additionally, if f is only declared, but not defined, clang accepts the code, which implies that the definition is not needed, and shouldn't be instantiated.
Is my reasoning correct, and this is a clang bug, or is instantiation required, and this is actually a gcc bug?
This is CWG #1581 I think, resolved by P0859.
temp.inst/5 says:
Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.
Does the existence affect the semantics of the program?
temp.inst/8 says:
The existence of a definition of a variable or function is considered to affect the semantics of the program if the variable or function is needed for constant evaluation by an expression ([expr.const]), even if constant evaluation of the expression is not required or if constant expression evaluation does not use the definition.
Is it needed for constant evaluation by an expression?
expr.const/15.6-7 says:
A function or variable is needed for constant evaluation if it is:
a constexpr function that is named by an expression that is potentially constant evaluated, or
a variable whose name appears as a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.
Is it named by an expression that is potentially constant evaluated?
expr.const/15.1-15.5 says:
An expression or conversion is potentially constant evaluated if it is:
a manifestly constant-evaluated expression,
a potentially-evaluated expression,
an immediate subexpression of a braced-init-list,
an expression of the form & cast-expression that occurs within a templated entity, or
a subexpression of one of the above that is not a subexpression of a nested unevaluated operand.
It is an immediate subexpression of a braced-init-list.

Why doesn't the linker emit an error in the code below?

I found the example below here. Clearly the comment in the snippet was wrong as the variable S::x is odr-used by the expression &S::x.
struct S { static const int x = 1; };
void f() { &S::x; } // discarded-value expression does not odr-use S::x
int main(){
f();
}
See live example
I understand the compiler doesn't need to emit such an error because [basic.def.odr]/10 says "no diagnostic required". But why doesn't the linker emit an error about the undefined variable S::x, as it does in the code below?
#include<iostream>
struct S { static const int x = 1; } s;
int main() {
std::cout << &s.x << '\n';
}
See live example.
But why doesn't the linker emit an error about the undefined variable S::x, as it does in the code below?
Because it is simply optimized out! An expression which result is never used and has no side effect will simply be ignored. And what was ignored must not be linked in. There is simply no code which reference the variable, also if the address of it was taken but then is not used.
As seen in your wandbox example, the compiler emits the correct diagnostic:
"expression result unused".
Code which is not in use will not result later in linker errors ;)
Your second example uses the value ( address of var ) and so there is a need to evaluate the expression. That emits code to the linker where the symbol of the address can not be found anywhere.
You ask:
[Why] doesn't the linker emit an error?
While saying:
[basic.def.odr]/10 says "no diagnostic required"
You answer your own question in "no diagnostic required". Not complying to the odr-rule is Undefined Behavior, the linker could raise an error or build a 4D version of Tetris and it still would be OK by the specs!
And to clarify about &S::x odr-using or not x:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
The address of an object is never a constant expression. S::x is odr-used in &S::x.
To justify that last assertion:
[expr.const]/6
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints [...]
and
[expr.const]/2.7
2) An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
2.7) an lvalue-to-rvalue conversion unless it is applied to
(none of the followinf points applies:)
2.7.1) a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
2.7.2)
a non-volatile glvalue that refers to a subobject of a string literal, or
2.7.3)
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
2.7.4)
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
Clearly the comment in the snippet was wrong as the variable S::x is odr-used by the expression &S::x.
And then that expression is dropped on the bit floor. &S::x; is not ODR-used. Once variant of [basic.def.odr] paragraph 3 reads (bold emphasis mine):
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
The section[expr] paragraph 11 covers the concept of a discarded-value expression:
In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded. The array-to-pointer and function- to-pointer standard conversions are not applied. The lvalue-to-rvalue conversion is applied if and only if the expression is a glvalue of volatile-qualified type and it is one of the following:
( expression ), where expression is one of these expressions,
id-expression,
subscripting,
class member access,
indirection,
pointer-to-member operation,
conditional expression where both the second and the third operands are one of these expressions, or
comma expression where the right operand is one of these expressions.
The statement &S::x; is a discarded-value expression that does not require lvalue-to-rvalue conversion, so there's nothing to convert. The statement might as well not exist, and hence doesn't exist as far as ODR-usage is concerned.
This metric can be changed by qualifying S::x as volatile rather than as const, and by trying to drop S::x on the bit floor:
struct S { static volatile int x; };
void f() { S::x; } // This discarded-value expression does odr-use S::x
int main(){
f();
}
The above compiles (but with a warning about a discarded expression) with GNU's C++ compiler/linker using --std=c++03 but fails to compile/link using --std=c++11 or higher. C++11 added quite a bit regarding the workings of the C++ abstract machine. Even though S::x; remains a discarded value expression, that S::x is now volatile requires the compiler to apply lvalue-to-rvalue conversion prior to dropping the result on the bit floor.
Change the S::x in the above to &S::x and the program once again compiles (but once again with a warning about a discarded expression). Even though S::x is volatile, it's address is not.

Private static array in C++ [duplicate]

This is a follow up question to Undefined reference to static constexpr char[][].
The following program builds and runs fine.
#include <iostream>
struct A {
constexpr static char dict[] = "test";
void print() {
std::cout << A::dict[0] << std::endl;
}
};
int main() {
A a;
a.print();
return 0;
}
However, if I change A::print() to:
void print() {
std::cout << A::dict << std::endl;
}
I get the following linker error in g++ 4.8.2.
/tmp/cczmF84A.o: In function `A::print()':
socc.cc:(.text._ZN1A5printEv[_ZN1A5printEv]+0xd): undefined reference to `A::dict'
collect2: error: ld returned 1 exit status
The linker error can be resolved by adding a line:
constexpr char A::dict[];
outside the class definition.
However, it's not clear to me why using one of the members of the array does not cause a linker error while using the array causes a linker error.
The standard does not require any diagnostics for a failure to provide a definition where one is required.
3.2 One definition rule [basic.def.odr]
4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. [...]
This means implementations are allowed to optimise away accesses to such variables, and that's what's happening in your first case with GCC.
Both GCC and clang have decided that they prefer a consistent user experience, where error messages about missing definitions do not depend on the optimisation level. Usually, that means that any missing definition causes an error message. However, in this case, GCC is doing some minimal optimisation even at -O0, avoiding the error.
But the program is an error either way, because even A::dict[0] is an ODR-use:
3.2 One definition rule [basic.def.odr]
3 A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5). [...]
The use of A::dict doesn't involve lvalue-to-rvalue conversion, it involves the array-to-pointer conversion, so the exception doesn't apply.
In addition to the information provided by #hvd in his answer...
From the C++ Draft Standard N3337 (emphasis mine):
9.4.2 Static data members
3 If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment- expression is a constant expression (5.19). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.
Given that A::data is odr-used in the expression A::data[0], as per the standard, it shall be defined in a namespace scope. The fact that g++ is able to successfully create a program without A::data being defined in a namescpace scope does not make the program right. To be standards compliant, A::data shall be defined.

How does odr-use apply to a variable identified by a qualified-id?

Sample code:
struct S
{
static const int a = 0;
};
int const *b = &S::a;
My understanding of the intent of the ODR is that S::a should be odr-used here, because its address is taken.
My question is: Where and how does the Standard specify that S::a is odr-used in this code?
My research so far: The only relevant part of the C++14 standard seems to be [basic.def.odr]/3:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
However I don't see how S::a fits into this. The definition of name ([basic]/4) excludes qualified-ids, so x can't refer to S::a here. I don't see any other text in the standard supporting the use of name to mean anything other than the definition from [basic]/4.
Maybe x means a; however a never appears as an expression, let alone a potentially-evaluated one: the a in S::a is an identifier, not an expression. (A qualified-id is a nested-name-specifier followed by an identifier, or some other things not relevant here).
I also don't understand why two different placeholders x and ex are used in this sentence. Saying "A appears as B" says to me that A and B are lexically identical but seeing A in the context of B has some extra meaning. At first I thought this might mean x is a more abstract way of talking about a variable (e.g. meaning the variable that is identified by S::a) and ex is an expression denoting that variable. However the quote goes on to say "Applying the lvalue-to-rvalue conversion to x", therefore x is actually an expression after all; so I don't see what the difference is between x and ex.
Further, I am not sure what "an expression e" refers to exactly. I guess it is an existential taken over all expressions in the program such that ex is a subexpression of e.