undefined reference to class static constexpr struct, g++ vs clang - c++

This is my code, a.cpp
struct int2
{
int x, y;
};
struct Foo{
static constexpr int bar1 = 1;
static constexpr int2 bar2 = {1, 2};
};
int foo1(){
return Foo::bar1; // this is ok for both clang++ and g++
}
int2 foo2(){
return Foo::bar2; // undefined reference to `Foo::bar2' in clang++
}
int main(){ std::cout << foo2().x << std::endl; return 0; }
use clang to compile, clang++ -std=c++11 a.cpp
/tmp/a-0dba90.o: In function `foo2()':
a.cpp:(.text+0x18): undefined reference to `Foo::bar2'
clang-7: error: linker command failed with exit code 1 (use -v to see
invocation)
g++ -std=c++11 a.cpp emits no error.
My question is,
who is right on the above code? clang or g++?
why bar2 is wrong while bar1 is correct in clang?
compiler version: g++ 5.4.0 and clang 7.0.0
UPDATE: The question is marked as a duplicate of another question, but it is not. I know I could add explicitly definition outside class to get it pass for clang. This question is about why all the difference between g++&clang.

You seem to assume that if one compiler is right, the other one must be wrong. The program either contains an error (and then the compiler that accepts it is wrong) or it doesn't (and then the compiler that rejects it is wrong). This in turn relies on an implicit assumption that the error in question, namely, missing definition of an ODR-used entity, is a diagnosable error. Unfortunately it isn't. The standard explicitly states that:
[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.
As problematic and unwanted this provision of the standard is, it's there. Your program has undefined behaviour because of a missing definition, and an implementation is not required to diagnose it. So both compilers are technically correct at any optimisation level.
In C++17 with mandatory copy elision the program no longer contains any ODR-use of the variable in question constexpr static data members are implicitly inline, no separate deinition needed (thanks Oliv).

Answer my own question.
I have some vague understanding about odr-use.
For literal type Foo::bar1, it is not odr-used, so it is fine.
For struct Foo::bar2: when return a struct inside a function body, it will invoke its copy constructor, which take a reference to Foo::bar2. So Foo::bar2 is odr-used, its definition must exist somewhere in the program otherwise it will result a link error.
But why g++ does not complain? I guess it is related to compiler optimization.
Verify my guess:
copy elision
add -fno-elide-constructors, g++ -fno-elide-constructors -std=c++11 a.cpp
/tmp/ccg1z4V9.o: In function foo2()':
a.cpp:(.text+0x27): undefined reference toFoo::bar2'
So, yes, copy elision will affect this.
But g++ -O1 still get passed.
function inline
add -fno-line, g++ -O1 -fno-elide-constructors -fno-inline -std=c++11 a.cpp
/tmp/ccH8dguG.o: In function foo2()':
a.cpp:(.text+0x4f): undefined reference toFoo::bar2'
Conclusion is both copy elision & function inline will affect its behavior. The different between g++ & clang is because g++ enabled copy elision by default but clang does not.

Related

static constexpr undefined reference error on clang

The following code compiles fine on Visual Studio 2019 and on gcc 10.2 (and other gcc versions) with -std=c++11 but fails on clang (versions 9, 10 and 11).
#include <map>
#include <string>
struct repo {
static constexpr const char *x = "sth";
};
int main() {
// 1) This compiles
std::map<std::string, int> m1 = { {repo::x, 3} };
// 2) This compiles
std::map<std::string, std::string> m2 = { std::make_pair(repo::x, "") };
// 3) This does not compile on clang
std::map<std::string, std::string> m3 = { {repo::x, ""} };
return 0;
}
The error from clang is:
... undefined reference to `repo::x'
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)
Compiler returned: 1
There are similar questions on SO on this, i.e. Undefined reference error, static constexpr data member, but none that explain to me why this code does not compile on clang. Is there an issue with the code in 3) above?
C++17 introduces the rule that static constexpr member variables are implicitly inline (P0386). Inline variables didn't exist before C++17.
This means that in earlier C++ standards, a compiler may need a static constexpr member variable to be defined. If its address is taken, for example.
In C++ standards earlier than C++17, you can ensure that your code is well-formed, by separately defining the static variable.
struct repo {
static constexpr const char *x = "sth";
};
constexpr const char *repo::x;
Edit:
It should be noted that with optimizations off, none of the examples successfully link.
It is an artifact of the optimizer that sometimes, a reference to a value can be flattened to the value itself, in which case the linker won't look for the missing symbol.
Using https://godbolt.org/z/edj6Ed I verified that the program compiles & runs for gcc >= 7, but fails with the same error as your clang for 6.4, provided that one uses
-std=c++17
(actually I used -pedantic -std=c++17).
Clang++ also compiles your program fine starting from version 5.0.0, the first to support C++17. It always fails with std=C++14.
What happened in C++17 that refers to static constexpr?
A constexpr specifier used in a function or static member variable (since C++17) declaration implies inline.
(see https://en.cppreference.com/w/cpp/language/constexpr )
See also: https://www.codingame.com/playgrounds/2205/7-features-of-c17-that-will-simplify-your-code/inline-variables
Pre-C++17, you need to have a definition of static constexpr variable if it is ODR-used.
In your example, x is ODR-used, since it is passed to as an argument to pair constructor, which takes values by reference. Thus, you need a definition for it.

Why does C++ allow you to reference a declared variable in the initial definition?

The following program compiles and runs just fine.
#include <iostream>
using namespace std;
int main() {
size_t foo = sizeof(foo);
cout << foo << endl;
return 0;
}
Using the compilation command
clang++ -o wut -Wall -Wextra -Werror -pedantic -ansi test.cc
I understand that this is probably the equivalent of
size_t foo;
foo = sizeof(foo);
Which makes more sense.
However, the initial statement breaks my mind a bit since I think of the RHS being evaluated prior to the LHS (or overall statement) being executed - which in this case might suggest that foo doesn't yet exist.
However, my assumption is that size_t foo always results in a declaration first, being broken out into its own statement during compile-time, followed by the definition.
Is my assumption correct? Is this governed/well defined in the specification(s)? If so, which section(s)? I realize I compiled with -ansi but this appears to work, without error, on all standard versions I tried it with.
You're looking for [basic.scope.pdecl]:
The point of declaration for a name is immediately after its complete declarator (Clause 11) and before its initializer (if any), except as noted below.
So once it gets to the =, the definition of foo is complete (which is also a declaration) and it can be referred to in its own initializer.

Linker error with static constant that doesn't seem to be odr-used

The definition in the standard for odr-used is pretty confusing when you get into the details (at least, for me it is). I generally rely on the informal definition of "If a reference is taken", except when an lvalue-to-rvalue conversion is available. For integral constants, they should be treated as rvalues, which seems like it should be excluded from the reference rule. Here is my sample code that is failing to link:
class Test
{
public:
Test();
static constexpr int MIN_VALUE { 5 };
int m_otherValue = 10;
};
Test::Test()
{
m_otherValue = std::max(m_otherValue, MIN_VALUE);
}
int main()
{
Test t;
}
And the linker error that I get:
clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
/tmp/main-e2122e.o: In function `Test::Test()':
main.cpp:(.text+0x2): undefined reference to `Test::MIN_VALUE'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Live sample: http://coliru.stacked-crooked.com/a/4d4c27d6b7683fe8
Why is the definition of MIN_VALUE required? It's just a constant to a literal value, the compiler should optimize this out as std::max(m_otherValue, 5). So I just don't get it.
std::max takes its arguments by reference, not by value. Performing the lvalue-to-rvalue conversion and then constructing a temporary object from that rvalue is not allowed. std::max could be checking that the two arguments are references to the same object, for all the compiler knows, and that check would be required to evaluate as true if called as std::max(MIN_VALUE, MIN_VALUE).
If you read a reference for std::max you will see that it takes it arguments by reference, and according to this odr-used reference
Informally, an object is odr-used if ... a reference is bound to it ...
Since you pass MIN_VALUE to a function which takes a reference, the member is odr-used and needs a separate definition.

Does Return Value Optimization need to declare a copy constructor

I have discovered that the Intel compiler does not generate return value optimization for std::array objects. The following code, which happen to be in the inner loop of my program is not optimized as it could.
std::array<double, 45> f(const std::array<double, 45>& y) {
auto dy_dt = std::array<double, 45>( );
...
return dy_dt;
}
I have figured out that this behaviour comes from the fact that my standard library implementation does not explicitly define a copy constructor for std::array. The following code demonstrates that:
class Test {
public:
Test() = default;
Test(const Test& x);
};
Test f() {
auto x = Test( );
return x;
}
When you compile it with
icpc -c -std=c++11 -qopt-report=2 test.cpp -o test.o
the report file shows
INLINE REPORT: (f(Test *)) [1] main.cpp(7,10)
which proves that the compiler generates RVO (the signature of f is changed so it can put the newly created object on the stack of the calling site). But if you comment out the line that declares Test(const Test& x);, the report file shows
INLINE REPORT: (f()) [1] main.cpp(7,10)
which proves that RVO is not generated.
In 12.8.31 of the C++11 standard that defines RVO, the example they give has a copy constructor. So, is this a "bug" of the Intel compiler or a conforming implementation of the standard?
This program causes undefined behaviour with no diagnostic required, due to violation of the One Definition Rule.
A copy-constructor is odr-used when returning by value -- even if copy elision takes place.
A non-inline function being odr-used means that exactly one definition of the function must appear in the program. However you provided none, and your declaration of the copy-constructor suppresses the compiler-generated definition.

class constexpr expressions in initializer lists

The following code fails to link with g++ 4.8.2:
#include <map>
struct Foo
{
constexpr static int foo = 1;
};
static std::map<int, int> map {{1, Foo::foo}};
int main()
{
return Foo::foo;
}
I get the following error:
g++ -std=c++11 -o foo foo.cc
/tmp/ccZXCwiK.o: In function `__static_initialization_and_destruction_0(int, int)':
foo.cc:(.text+0x51): undefined reference to `Foo::foo'
If I comment out the map, things link just fine. Is this a compiler bug, or is it some corner case I am missing in the standard?
You forgot to define the static member.
As soon as you odr-use it, a full definition is required. Yes, even though it has an inline initialiser and, yes, even though it's marked constexpr.
Add:
constexpr int Foo::foo;
(example)
Your original code works for me in GCC 4.8.1 via both ideone.com and Coliru, but that's with optimisations turned on (so that constant is likely being supplanted for the symbol in each case). With optimisations off, the error is reproducible.