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.
Related
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.
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.
EDIT: I'm not asking how to make the code work, but why it has error. Also, the static member is not only declared inside the class, but also has been initialized with a value (at least it appears to have), hence I don't think it's redundant. Also, this question asks beyond that, so please don't close it. It is not a duplicate.
Say class A has a static member data defined inside. The shell record below does not link (ld: undefined reference).
Note that since C++11, std::vector<>::push_back() accepts either a const lvalue reference (const value_type&) or an rvalue reference (value_type&& val). Moreover, even if it doesn't accept an rvalue reference, an rvalue can be bound to a const lvalue reference.
$ cat class.h
struct A {
static const int STAT_MEMBER = 1;
A();
int a;
};
$ cat class.cc
#include "class.h"
#include <vector>
using namespace std;
A::A() { a = STAT_MEMBER; }
int main() {
vector<int> v;
v.push_back(A::STAT_MEMBER);
}
$ g++ -std=c++14 class.cc -o class
/tmp/ccNXDnyu.o: In function `main':
class.cc:(.text+0x2f): undefined reference to `A::STAT_MEMBER'
collect2: error: ld returned 1 exit status
$
Question:
(1) is A::STATIC_MEMBER an rvalue? (but regardless of it being an rvalue or an lvalue, it shouldn't cause an error in linker -- it should have been fine or have caused an error in compiler)
(2) why didn't the compiler/linker complain about STATIC_MEMBER in A::A()?
(3) why didn't the compiler complain about STATIC_MEMBER in main()?
(4) why did the linker complain about STATIC_MEMBER in main()?
I know this problem should be gone if the static member is declared inside the class but defined outside the class, but I'm mostly interested the problem above.
G++ version: g++-6 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04) 6.3.0 20170519
The reason is quite easy:
If you use a value, the compiler can use the known value to your undefined but declared static const member.
But the vector::push_back takes the reference to the value! The reference is the address and there is no address of an undefined object, only the value of the object is known.
You can reduce the code to this, which simplifies it a bit:
struct A {
static const int STAT_MEMBER = 1;
};
void Works( const int i ){} // this takes the VALUE of the variable
void Fail( const int& i){} // this takes the ADDRESS of the variable
// but there is no object which can be
// addressed, because it is not defined!
int main()
{
Works( A::STAT_MEMBER );
Fail( A::STAT_MEMBER );
}
Simply comment in and out to get linker error or not!
This only works in C++17, so just wait a little bit (or turn on the C++17 support flag in your compiler).
From the cppreference.com:
A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:
struct X
{
inline static int n = 1;
};
This can be simplified to this:
struct A { static const int s = 1; };
int main()
{
int a1 = A::s; // OK
int const & a2 = A::s; // error,
int const * a3 = &A::s; // same thing
}
a1 case works because of the special treatment that integral constants receive. Basically compiler knows the value of s at compile time and does not need to access storage. This also allows s to be used as template parameters:
::std::array<int,A::s> xx;
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.
consider the below minimal example.
#include<iostream>
struct A
{
A(){std::cout<<"def"<<'\n';}
void foo()&{std::cout<<"called on lvalue"<<'\n';}
};
int main()
{
A a;
a.foo();
A().foo();
return 0;
}
this gives error about expecting ';' at the end of declaration and and expected un-qualified-id before '{'.
Can i know what i'm doing wrong? in the actual code i want to avoid calling the non-static member function through temporaries.
tried on gcc 4.7.2 and vc2010.
Thanks.
The answer will be short: VC10 and GCC 4.7.2 do not support ref-qualifiers.
However, notice that your foo() function has an lvalue ref qualifier, meaning that you cannot invoke it on temporaries.
If you want this expression to compile as well:
A().foo();
Then you should use const&, or provide an overload for &&, as in this live example.
To work with ref-qualifiers you can use Clang 3.2 or GCC 4.8.1.