Should Taking the Address of a Templatized Function Trigger its Compilation? - c++

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.

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.

Templates and ODR

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.

Does the comma operator odr-use its arguments?

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.

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.