"static const int" causes linking error (undefined-reference) - c++

I am baffled by the linker error when using the following code:
// static_const.cpp -- complete code
#include <vector>
struct Elem {
static const int value = 0;
};
int main(int argc, char *argv[]) {
std::vector<Elem> v(1);
std::vector<Elem>::iterator it;
it = v.begin();
return it->value;
}
However, this fails when linking -- somehow it needs to have a symbol for the static const "value."
$ g++ static_const.cpp
/tmp/ccZTyfe7.o: In function `main':
static_const.cpp:(.text+0x8e): undefined reference to `Elem::value'
collect2: ld returned 1 exit status
BTW, this compiles fine with -O1 or better; but it still fails for more complicated cases. I am using gcc version 4.4.4 20100726 (Red Hat 4.4.4-13).
Any ideas what might be wrong with my code?

If you want to initialize it inside the struct, you can do it too:
struct Elem {
static const int value = 0;
};
const int Elem::value;

Try writing it as
struct Elem {
static const int value;
};
const int Elem::value = 0;
etc
.

static class members are generally supposed to be defined outside the class (declared inside, defined outside) in one compilation unit.
I don't remember how that interacts with inline initialization of const static integral members.

Also see this post: essentially, the problem is that somehow compiler ends up expanding your code into taking the address of Elem::value.

Why not just do this?
return Elem::value;
But the answer is that you are assigning a value in the declaration. This is supposed to work for basic types such as int, and is only required for complex types (i.e. classes, such as if you had a string instead of int). What I have found in practice is that this is hit or miss depending on what version of what compiler you are using. And, as you found out, which optimization level.

In most of the compilers defining in-class static const <integral type> works fine. But some compilers like Android NDK, such in-class definitions results in linker errors. For such case, we may use the typed enums:
struct X
{
enum : int64_t { VALUE = 100; }; // == static const int64_t VALUE = 100;
};

Related

Link error when using std::make_unique when creating object with const char *

Apologies for not providing simple runnable failure code. The error is part of the larger codebase that would require a lot of refactoring.
I'm running into a very weird linking problem with my code that so far I can't solve.
I have a class with static constexpr const char * for some strings and local std::sunique_ptr. The pointer is to a different templated class which contains another templated class (#2).
The main class is like this (abridged):
class Manager {
public:
Manager();
virtual ~Manager();
private:
// Topic Constants
static constexpr const char* kActuatorsCommand = "ActuatorsCommand";
static constexpr const char* kActuatorsProxy = "ActuatorsProxy";
std::unique_ptr<DataReader> faults_;
};
So the DataReader constractor takes two const string & parameters.
If I declare faults_ as a regular old pointer and create it with new the code runs and links just fine: DataReader *faults_ = new DataReader<uint32_t>(kActuatorsCommand, kActuatorsProxy).
However, if I use std::make_unique the linker complains that there is an undefined reference to those static const char* strings, even though they are in the header of the class.
Also, If I remove the #2 class everything links fine.
Using gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
I understand it may be rather vague question, but some direction where to look would be appreciated.
Also, this question may be similar to this one. However, in my case everything is on one binary.
Update: finally found how to reproduce it.
class DataReader {
public:
explicit DataReader(const std::string& topic, const std::string& library_name)
: topic_(topic),
library_name_(library_name) {
}
private:
const std::string name_;
const std::string topic_;
const std::string library_name_;
};
#include <memory>
#include "DataReader.h"
class Manager {
public:
Manager();
virtual ~Manager();
private:
// Topic Constants
static constexpr const char* kActuatorsCommand = "ActuatorsCommand";
static constexpr const char* kActuatorsProxy = "ActuatorsProxy";
std::unique_ptr<DataReader> faults_;
};
Manager::Manager() {
faults_ = std::make_unique<DataReader>(kActuatorsCommand, kActuatorsProxy);
}
Manager::~Manager() {}
The code fails to link when compiled with -o0. With -03 it links fine.
g++ -O0 -Wall -Wconversion -lstdc++ -pthread -std=c++14 -o ex3 src/ex3.cpp
/tmp/ccJebZ18.o: In function `Manager::Manager()':
ex3.cpp:(.text+0x41): undefined reference to `Manager::kActuatorsProxy'
ex3.cpp:(.text+0x48): undefined reference to `Manager::kActuatorsCommand'
collect2: error: ld returned 1 exit status
Makefile:8: recipe for target 'ex3' failed
I think it has something to do with inlining
A constexpr specifier used in a function or static member variable
(since C++17) declaration implies inline
When you compile the code with -O3 or -O0 -std=c++17 the static variable might be inlines which avoids the undefined reference.
I did some digging and found:
You can take the address of a static member if (and only if) it has an
out-of-class definition
From Bjarne Stroustrup's C++ faq.
Adding the following code to your example outside of the class body made it compile using -std=c++14:
constexpr const char* Manager::kActuatorsCommand;
constexpr const char* Manager::kActuatorsProxy;

How do I define a static constexpr array member of a template class specialisation

I am attempting to use static constexpr arrays within a template specialised class as shown below:
///////////////////////////////////////////////////////////////////////////
struct good {
static constexpr int values[1] = { 0 };
};
constexpr int good::values[1];
///////////////////////////////////////////////////////////////////////////
template <typename T>
struct bad;
template <>
struct bad<int> {
static constexpr int values[1] = { 0 };
};
constexpr int bad<int>::values[1];
///////////////////////////////////////////////////////////////////////////
int
main (int argc, char **argv)
{
#if 1
return good::values[0];
#else
return bad<int>::values[0];
#endif
}
I am aware of the requirement to both declare and define static members, and the 'good' case from above appears to succeed with gcc-6.2.0 and clang-3.9.0 using -std=c++1z.
However the 'bad' case results in an undefined reference under clang-3.9.0, giving the output as follows:
danny#steve ~/src $ clang++ -std=c++1z scratch.cpp
/tmp/scratch-56fa44.o: In function `main':
scratch.cpp:(.text+0x15): undefined reference to `bad<int>::values'
clang-3.9: error: linker command failed with exit code 1 (use -v to see invocation)
Is there something additional I need to do, or that I'm missing, to instantiate static constexpr members? The code appears to work fine when modified to use static const members.
The code will link successfully if the unit is instead compiled with -std=c++1y.
Is there something additional I need to do, or that I'm missing, to instantiate static constexpr members?
The code looks well formed, but it indeed fails to link in Clang up to version 5.0.2, however it is fully accepted in Clang 6.0. Demo: https://gcc.godbolt.org/z/Ka7W4bbqf
This suggests that it was just a compiler bug.

static variable declaration and definition in C++

I have a sample code as shown below:
class myclass
{
public:
static int j;
myclass(){};
~myclass(){};
};
int main(int argc, char** argv) {
myclass obj;
return EXIT_SUCCESS;
}
Now I have declared a static int inside myclass and although I have not defined it, the compiler does not give me any errors until I began using the static variable. Why is that?
Because these are linker errors, not compiler errors. Linker errors never arise until you use an undefined symbol.
"the compiler does not give me any errors until I began using the static variable. Why is that?"
Because it didn't need to be linked with your code until that point (when you start to use it). Unused code is ignored / stripped off by the linker.

C++ const circular declaration

Why does the following code compile with a circular const variable referencing itself?
#include <iostream>
extern int main(int argc, char* argv[])
{
const int foo = foo * 60;
std::cout << foo << std::endl;
return 0;
}
I'm compiling on a Solaris 5.10 x86 host with the SUNWspro compiler:
/opt/SUNWspro/bin/CC test.cpp
For completeness, this is what it prints:
$ ./a.out
-519270512
In C++, variables are in scope and can be used as part of their own initializers. For example, the following code is also legal:
int x = x;
Doing this results in undefined behavior, since you're referring to the value of x before it has been initialized. However, it's perfectly legal code. I think that the rationale behind this is that you might in some cases want an object to refer to itself during its construction. For example, you could conceivably do something like this:
MyObject x(137, &x); // Pass a pointer to x into its own constructor
I'm honestly not sure why you'd ever want to do this, though.
Hope this helps!

static ints, compilation units and the ternary operator

// SomeCls.h
class SomeCls
{
static const int PERIOD_ALARM_NORMAL = 5;
static const int PERIOD_ALARM_THRESH = 1;
void method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
} obj;
It is going to build ok. Now take out the method() implementation and place it in a cpp file:
//SomeCls.cpp
#include "SomeCls.h"
void SomeCls::method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
Why does mr. linker say
undefined reference to SomeCls::PERIOD_ALARM_NORMAL' undefined
reference toSomeCls::PERIOD_ALARM_THRESH'
?
Thanks
EDIT:
It seems to me that that inside .h, the ternary operator takes static const ints it as rvalues but ... outside the decalrative .h, it regards them as lvalue and needs definition.
This is what I have managed to understand from the answers below. Kudos to Bada compiler (some eabi linux thinggie)
If the compiler can't see all the static class constants' values, then you have to provide definitions for them so that they'll actually be stored somewhere. Add the following to your cpp file:
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
This is a GCC limitation, but it's completely standard comforming. Technically a static const int is still an lvalue. You've provided the value inline so compiler will almost always use it as an rvalue. There is one exception. The abstract instructions emitted by the compiler for ternary operators queries the address of lvalues. Hence the error you're seeing.
You can work around this by using enum instead. Or if you're using a new version of GCC constexpr was added to standard to fix this exact problem (named and typed rvalues).
Alternatively you can provide the linker with a definition for the constants. E.g. in your classes cpp file add the a line like
// I wish I had constexpr
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
As a side note: I was a staunch proponent of static const for class scope constants. Then I found out that MSVC doesn't allow for static const float with the value inline. So the only values you can portably put in a static const are integers, in which case enums provide all the same features plus the guarantee that they'll never silently convert to an lvalue.
If, for whatever reason, you compiler simply refuses to link the code (like GCC 4.4.5 does), here's a simple fix: Replace the static const ints with an enum.
// someclass.h
// include guards, blabla
class SomeClass
{
enum AlarmPeriod{
PERIOD_ALARM_NORMAL = 5,
PERIOD_ALARM_THRESH = 1
};
public:
void method();
};
// someclass.cpp
#include "someclass.h"
void SomeClass::method(){
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
// main.cpp
#include "someclass.h"
int main(){
someclass sc;
sc.method();
}
This links cleanly with GCC 4.4.5, which wouldn't link the former version, even though both are technically the same.
Note that you cannot, amongst other things, take the address of PERIOD_ALARM_NORMAL and PERIOD_ALARM_TRESH anymore, because both names are just aliases for their respective values.