I am a bit confused by the static in-class initialization of a const member. For example, in the code below:
#include <iostream>
struct Foo
{
const static int n = 42;
};
// const int Foo::n; // No ODR
void f(const int& param)
{
std::cout << param << std::endl;
}
int g(const int& param)
{
return param;
}
template<int N>
void h()
{
std::cout << N << std::endl;
}
int main()
{
// f(Foo::n); // linker error, both g++/clang++
std::cout << g(Foo::n) << std::endl; // OK in g++ only with -O(1,2 or 3) flag, why?!
h<Foo::n>(); // this should be fine
}
Live example
I do not define Foo::n (the line is commented). So, I expect the call f(Foo::n) to fail at link time, and indeed it does. However, the following line std::cout << g(Foo::n) << std::endl; compiles and links fine only by gcc (clang still emits a linker error) whenever I use an optimization flag such as -O1/2/3.
Why does gcc (tried with gcc5.2.0 and gcc 4.9.3) compile and link the code when the optimization is turned on?
And am I correct to say that the only usage of in-class static const members is in constant expressions, such as template parameters like in the h<Foo::n> call, in which case the code should link?
I suppose that the compiler performs the following actions during the optimization:
The value const static int n is inlined everywhere. No memory is allocated for the variable n, references to it becomes invalid. The function f() need a reference to n so the program is not compiled.
The function g is short and simple. It is effectively inlined and optimized. After the optimization, the function g does not need a reference to n, it just returns constant value 42.
The solution is to define the variable outside the class:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
ODR violations do not require a diagnostic, from the draft C++ standard standard section 3.2 [basic.def.odr] (emphasis mine going forward):
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required.
So inconsistent behavior at different optimization levels is perfectly conformant behavior.
Informally a variable is odr-used if:
its address is taken, or a reference is bound to it, and a function is odr-used if a function call to it is made or its address is taken. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.
So both f and g will be odr-uses and require a definition.
The relevant C++14 quote on odr-use would be from section [basic.def.odr]:
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 nontrivial
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 [...]
The wording in C++11 is similar, the changes from C++11 to C++14 are reflected in defect report 712.
Before C++11 it is a bit more complicated but in principle the same for this case.
Formally, ODR violations are undefined behaviour, so the compiler may exhibit any behaviour it likes. That's why the behaviour changes with optimization level and compiler- the compiler has no obligation to maintain a particular behaviour.
There is no definition at all. GCC 4.9.2 doesn't compile and link that with any flags.
Note, that:
const static int n = 42;
is a declaration and initializer, but not a definition.
Related
Consider the following code:
class Test {
public:
static const int VALUE = 100;
};
std::tuple<int> foo(std::tuple<int> value) {
return value;
}
int main()
{
auto t = std::make_tuple(Test::VALUE); // This compiles fine
auto t1 = foo(std::make_tuple(Test::VALUE)); // undefined reference to `Test::VALUE' linker error
}
According to the other question (What does it mean to "ODR-use" something?) and the answer:
In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present.
In the first case (variable t) the variable VALUE is not odr-used because only it' value is needed. But in the second case why the code won't compile? If I would pass t instead of passed rvalue(?) the code would compile fine. How the second case differs from the first and why VALUE is odr-used here?
make_tuple takes the argument by const int& (since it's a constant lvalue and it takes a T&& argument), and binding a reference to a value is an ODR-use.
Both cases are ill-formed no diagnostic required. At high optimisation levels of gcc for example, the entire program is optimised out and there are no linker errors, where at no optimisation, both statements give linker errors.
To make it not an ODR use, you can convert it to an rvalue:
// Various ways to force the lvalue-to-rvalue conversion
auto t = std::make_tuple(int(Test::VALUE));
auto t1 = foo(std::make_tuple((void(), Test::VALUE)));
auto t2 = foo(std::make_tuple(+Test::VALUE));
(So std::make_tuple takes an int&& of a temporary)
Or you can make the definition inline (Easiest with constexpr):
class Test {
public:
static constexpr int VALUE = 100;
};
In the first case (variable t) the variable VALUE is not odr-used because only it' value is needed.
No. It's value is needed and it is ODR used.
But in the second case why the code won't compile?
Because the compiler you are using is not smart enough to optimize the second snippet of code. For example, both lines compile fine on gcc9.3 with -O1.
Why constexpr does not work with std::cout, but works with printf?
#include <iostream>
constexpr void f() { std::cout << ""; } //error
constexpr void g() { printf(""); } //ok
And why std::cout works with lambdas constexpr?
#include <iostream>
int main () {
auto h = []() constexpr { std::cout << ""; }; //ok
}
Technically, it doesn't work with any of them.
From [dcl.constexr]:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
f() and g() are never constant expressions (neither std::cout << x nor printf() are constexpr functions), so the constexpr declaration is ill-formed. But the compiler isn't required to diagnose this (in this case, it may be easy, but in the general case... not so much). What you're seeing is that your compiler was able to diagnose one problem but not the other.
But they're both wrong.
It doesn't. You need to use it to force a compile time error.
constexpr int a = f(), 0; // fails
constexpr int b = g(), 0; // fails
constexpr functions that never produce a constant expression are ill-formed; no diagnostic required. This means that compilers do a best effort check to see if that is the case, but your program already has an error either way. Seems like gcc can't see that printf is not a constant expression. clang errors at the definition.
When I try to use a static const to initialize a unique_ptr, I get an "undefined reference" error. However, when I new a pointer using the same constant, the symbol seems to be magically defined.
Here is a simple program that reproduces the error:
Outside_library.h
class Outside_library
{
public:
static const int my_const = 100;
};
main.cpp
#include "Outside_library.h"
#include <iostream>
#include <memory>
class My_class
{
public:
My_class(int num)
{
m_num = num;
};
virtual ~My_class(){};
private:
int m_num;
};
int main(int, char* [])
{
My_class* p_class = new My_class(Outside_library::my_const);
delete p_class;
// ==== ERROR HERE: When I comment this line out, the program runs fine.
std::unique_ptr<My_class> p_unique
= std::make_unique<My_class>(Outside_library::my_const);
std::cout << "I made it through!" << std::endl;
return 0;
}
I compiled the program using
g++ main.cpp -std=c++14
and got the following error.
/tmp/ccpJSQJS.o: In function `main':
main.cpp:(.text+0x51): undefined reference to `Outside_library::my_const'
collect2: error: ld returned 1 exit status
Can someone please help me understand why the constant is defined when using new, but not when using make_unique?
C++ has something known as the One-Definition Rule (ODR):
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; a reference is odr-used if it is used and its referent is not known at compile time; and a function is odr-used if a function call to it is made or its address is taken. If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error.
The linked site gives the following example:
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
Your explicit constructor invocation does not "odr-use" Outside_library::my_const but the call to std::make_unique() does. When an object is odr-used it must have exactly one definition (not declaration). Your example has a declaration only. Again from cppreference:
a variable x in a potentially-evaluated expression ex is odr-used unless both of the following are true:
applying lvalue-to-rvalue conversion to x yields a constant expression that doesn't invoke non-trivial functions
either x is not an object (that is, x is a reference) or, if x is an object, it is one of the potential results of a larger expression e, where that larger expression is either a discarded-value expression or has the lvalue-to-rvalue conversion applied to it
The solution as suggested by Jarod42 is to use constexpr instead of const (if you have control over the "outside library" code). If you do not, then you'll need to link the program against the library that contains the definition of Outside_library::my_const.
g++ main.cpp -std=c++14 -lOutside_library
make_unique takes (forwardind) reference of its parameter, and so odr-use it.
My_class(Outside_library::my_const) only use the value.
One solution is to define the member in a TU:
const int Outside_library::my_const;
or using constexpr value (since C++11):
class Outside_library
{
public:
static constexpr int my_const = 100;
};
I understand that static const members have to have out-ouf-class definition it they are odr-used. But the thing is, my program compiles and run just fine even without members definition.
Let's take a look at this example from C++ FAQ:
class AE
{
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
void f()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
cout << *p1 << endl;
}
int main()
{
f();
const int* p1 = &AE::c6;
std::cout << p1 << "\n";
return 0;
}
//RESULT:
// 7
// 00007FF735E7ACE8
I see no error whatsoever. I use Visual Studio 2015, and this code compiles and runs just fine.
My question: Is this specific to msvc or there are some language changes that I'm not aware of?
UPDATE: This is not a duplicate, as I said at the very beginning: I do understand how this suppose to work, I don't understand why it doesn't work as it should.
The program explicitely violates the one definition rule as you have stated in your question. But the standard does not require a diagnostic in this case. This is explicit in 3.2 One definition rule [basic.def.odr] §4 (emphasis mine)
Every program shall contain exactly one definition of every non-inline fonction or variable that is odr-used
in that program; no diagnostic required.
and 1.4 Implementation compliance [intro.compliance] §2.3 says:
If a program contains a violation of a rule for which no diagnostic is required, this International
Standard places no requirement on implementations with respect to that program.
That means that gcc is right to choke on that program because one rule has been violated. But it is not a bug when MSVC accepts it as a compiler extension(*) without even a warning because the standard places no requirement here.
We are in the compile time undefined behaviour named ill-formed program, no diagnostic required. A compiler implementation is free to do what it wants:
reject the program
automatically fix the error and generate the code as if the definition was present (for current case)
remove the offending line[s] if it makes sense and makes the program compilable - even if what is generated is not what the programmer expected
compile it into a program that ends in a run-time error
[add whatever you think of, but I have never seen a compiler able to hit my cat...]
(*) more exactly, it is a compiler extension if it is documented. Unfortunately, I currently have no MSVC compiler full documentation, so I can not say whether it is documented and is a conformant extension, or is not and is just hmm... one possible execution of the program
Here is a slight variation on OP's code demonstrating that the compiler did provide a definition:
#include <iostream>
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int main()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
std::cout << *p1 << "(" << p1 << ") - " << *p2 << "(" << p2 << ")" << std::endl;
return 0;
}
The output is (with an old MSVC 2008, debug mode to avoid as much optimization as possible):
7(00DF7800) - 31(00DF7804)
that is expected values and consecutive addresses
With same code, Clang 3.4 complains (as expected) at link time with
undefined reference to `AE::c6'
It just so happens that your compiler has applied some optimisations that mean it doesn't require you to follow the One Definition Rule in this case.
However, the C++ standard still requires you to do so, so your program has undefined behaviour. The standard does not require a compiler to tell you when you violate this particular rule, so it's chosen not to. Indeed, it would have to put in extra effort to detect the problem.
The C++ standard says ([basic.def.odr] 3.2 paragraph 2) "A variable
whose name appears as a potentially-evaluated expression is odr-used
unless it is an object that satisfies the requirements for appearing
in a constant expression (5.19) and the lvalue-to-rvalue conversion
(4.1) is immediately applied."
Read this for further information
Please consider the following two C++14 programs:
Program 1:
struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
Program 2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
Are neither, either or both of these programs ill-formed?
Why/why not?
Both programs are well-formed. The C++14 standard requires that s.f() be a constant expression because it is being used to initialize a constexpr variable, and in fact it is a core constant expression because there's no reason for it not to be. The reasons that an expression might not be a core constant expression are listed in section 5.19 p2. In particular, it states that the evaluation of the expression would have to do one of several things, none of which are done in your examples.
This may be surprising since, in some contexts, passing a non-constant expression to a constexpr function can cause the result to be a non-constant expression even if the argument isn't used. For example:
constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr auto y = f(x); // ill-formed
}
However, the reason this is ill-formed is because of the lvalue-to-rvalue conversion of a non-constant expression, which is one of the things that the evaluation of the expression is not allowed to do. An lvalue-to-rvalue conversion doesn't occur in the case of calling s.f().
I can't seem to find a compelling passage or example in the standard that directly addresses the issue of calling a constexpr member function on a non-constexpr instance, but here are some that may be of help (from draft N4140):
[C++14: 7.1.5/5]:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
From this I take that the program is not outright ill-formed just because a constexpr function has a possible non-constexpr path.
[C++14: 5.19]:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
This is somewhat closer to your example programs, here a constexpr constructor may reference a non-constexpr variable depending on the value of the argument, but there is no error if this path is not actually taken.
So I don't think either program you presented should be ill-formed, but I cannot offer convincing proof :)
This sounds like a quiz question, and not presented by a student, but the professor testing the public on stackoverflow, but let's see...
Let's start with the One Definition Rule. It's clear neither version violates that, so they both pass that part.
Then, to syntax. Neither have syntax failures, they'll both compile without issue if you don't mind the potential blend of a syntax and semantic issue.
First, the simpler semantic issue. This isn't a syntax problem, but f(), in both versions, is the member of a struct, and the function clearly makes no change to the owning struct, it's returning a constant. Although the function is declared constexpr, it is not declared as const, which means if there were some reason to call this as a runtime function, it would generate an error if that attempt were made on a const S. That affects both versions.
Now, the potentially ambiguous return g(S()); Clearly the outer g is a function call, but S may not be so clear as it would be if written return g(S{}); With {} initializing S, there would be no ambiguity in the future should struct S be expanded with an operator() (the struct nearly resembles a functor already). The constructor invoked is automatically generated now, and there is no operator() to create confusion for the compiler at this version, but modern C++14 is supposed to offer clearer alternatives to avoid the "Most Vexing Parse", which g(S()) resembles.
So, I'd have to say that based on semantic rules, they both fail (not so badly though).