static constexpr undefined reference error on clang - c++

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.

Related

C++ "volatile struct" issue: compiles with gcc 11 but not with earlier versions

The following code compiles using gcc 11+ but fails to compile with gcc version <= 10.
#include <stdint.h>
typedef volatile struct s {
uint32_t receiver:1;
uint32_t transmitter:1;
uint32_t _res:30;
} s_t;
s_t get_struct(void){
struct s m = {0};
return m;
}
int main() {
// compiles using gcc 11
// does not compile using gcc 10 and lower
s_t io1 = get_struct();
return 0;
}
You can try for yourself here:
https://godbolt.org/z/cavYxKdKo
Can you please explain why this is?
FYI, the code compiles with earlier gcc version if the individual struct members are qualified with volatile (instead of the struct itself). I don't know why this is as I thought the semantics are the same (at least they are in C).
typedef struct s {
volatile uint32_t receiver:1;
volatile uint32_t transmitter:1;
volatile uint32_t _res:30;
} s_t;
Similar questions:
volatile struct = struct not possible, why?
Copy constructor for C volatile bitfield struct
Thanks to helpful comments to my question, I try to answer the question myself with the best of my knowledge. Please comment if you spot mistakes.
Whether the code compiles or not has to do with the C++ version used.
The given code
compiles with C++17 (use gcc 11 or gcc 10 with option -std=c++17) and
does not compile with C++14 (use gcc 10 or gcc 11 with option -std=c++14).
The difference between both C++ versions with regard to this issue is based on "mandatory elision of copy/move operations" starting with C++17, see here.
Using C++14, appropriate constructors need to be defined in order to construct s_t io1.
To see this more easily, in the linked example switch to "x64-64 clang 13.0.0)". This compiler provides diagnostic messages that are easier to comprehend in my opinion. "clang" compiles the code when given the option -std=c++17. Otherwise, it emits error messages:
error: no matching constructor for initialization of 's_t' (aka 'volatile s')
It also lists several implicitly defined constructors that were rejected as "not viable".
In other words, we need to provide the proper constructor manually (aka. user-defined constructor).
First, we need to take care of the initialization of struct s m = {0}; because as soon as we add any user-define constructors, there won't be an implicitly defined constructor any more. See default constructor. Further, aggregate initialization does not apply any more as soon as there are user-defined constructors added to the struct.
This will do s(int i) {} for compiling (let's omit initialization or other code from constructors).
Second, we will need to provide a constructor for argument type s_t.
If I declare a constructor s(volatile struct s l) {}
"clang" complains with
error: copy constructor must pass its first argument by reference
s(volatile struct s l) { }
If I declare a copy constructor s(volatile struct s & l) { }
"clang" complains with
error: no matching constructor for initialization of 's_t' (aka 'volatile s')
However, if I declare a move constructor s(volatile struct s && l) { }
the code compiles (with clang and gcc, C++14 and C++17).
This is the resulting code to try for yourself.

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

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.

Argument only used in unevaluated context within the body of a constexpr function

The following piece of code has successfully compiled with gcc 5.3.0 but has failed to compile with clang 3.7.0. I used the online coliru compilers with the same command line options in both cases: -std=c++14 -O2 -Wall -pedantic -pthread.
#include <cstdio>
// Definition of constexpr function 'foo'.
constexpr std::size_t foo(const int& arg_foo) { return sizeof(arg_foo); }
// Definition of function 'test'.
void test(const int& arg)
{
// The following line produces an error with clang.
constexpr std::size_t res_foo = foo(arg);
// Print the result returned by the 'foo' function.
std::printf("res_foo = %lu\n", res_foo);
}
// Definition of function 'main'.
int main(int argc, const char* argv[])
{
// Test function call.
test(argc);
// Return statement.
return 0;
}
clang rejects it with the following error:
error: constexpr variable 'res_foo' must be initialized by a constant expression
constexpr size_t res_foo = foo(arg);
~~~~^~~~
Because of this difference between the two compilers, I am wondering if this is a valid piece of code. If not, I would like to get a better understanding of why this is the case.
You are mixing const and constexpr values together. The definition of constexpr is a value known at compile time. But the argcvariable is only known at run time (it is a number of arguments passed to your executable). So you cannot assign it to another constexpr variable - res_foo. It is Removing constexpr from the res_foo definition will make your code compilable.
The difference between const and constexpr can be simplified to something like this:
const - I'm not going to change this value
constexpr - This value is known at compile time and I'm not going to change it
My guess is that GCC is able to compile this code with O2 since you are not using the arg_foo argument and the size of it is known at the compile time. But it is still syntactically incorrect - compiler should emit an error, since non-constexpr value is assigned to a constexpr variable.
The program is well-formed, since foo(arg) is a prvalue core constant expression as defined in C++14 5.20/2. In particular, there is no lvalue-to-rvalue conversion during the evaluation, which would make it not a constant expression.

Could non-static member variable be modified in constexpr constructor (C++14)?

struct A {
int a = 0;
constexpr A() { a = 1; }
};
constexpr bool f() {
constexpr A a;
static_assert(a.a == 1, ""); // L1: OK
return a.a == 1;
}
static_assert(f(), ""); // L2: Error, can not modify A::a in constexpr
Online Compiler URL: http://goo.gl/jni6Em
Compiler: clang 3.4 (with -std=c++1y)
System: Linux 3.2
If I delete L2, this code compiles. If I add L2, the compiler complained "modification of object of const-qualified type 'const int' is not allowed in a constant expression". I am not a language lawyer, so I am not sure whether this is true. However, if it is, why compiler didn't complain anything about L1, since it also called A() as constexpr? Is this a bug of clang? Or did I miss anything?
Reference: http://en.cppreference.com/w/cpp/language/constexpr
BTW, if I change "constexpr A a;" to "A a;" (remove constexpr keyword), L1 failed to compile which is expect. However, the compiler didn't complain about L2 anymore.
Online Compiler URL about this: http://goo.gl/AoTzYx
I believe this is just a case of compilers not having caught up to the changes proposed for C++14. Your constexpr constructor satisfies all the conditions listed in ยง7.1.5/4 of N3936. Both gcc and clang fail to compile your code, but for different reasons.
clang complains:
note: modification of object of const-qualified type 'const int' is not allowed in a constant expression
which doesn't make much sense, but reminds me of the C++11 restriction that constexpr member functions are implicitly const (this is a constructor, and that doesn't apply, but the error message is reminiscent of that). This restriction was also lifted for C++14.
gcc's error message is:
error: constexpr constructor does not have empty body
Seems pretty clear that gcc still implements the C++11 rules for constexpr constructors.
Moreover, N3597 lists this example:
struct override_raii {
constexpr override_raii(int &a, int v) : a(a), old(a) {
a = v;
}
constexpr ~override_raii() {
a = old;
}
int &a, old;
};
N3597 was superseded by N3652, which contains the wording found in the current draft. Unfortunately, the earlier example disappears, but, again, nothing in the current wording says you cannot assign values to data members within the body of a constexpr constructor.
Update (2017-10-03)
clang fixed this, but there has been no new release yet: https://bugs.llvm.org/show_bug.cgi?id=19741
(Compiler explorer)

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.