Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined reference error when initializing unique_ptr with a static const

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?

like image 439
eswens13 Avatar asked Jan 24 '18 17:01

eswens13


1 Answers

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:

  1. 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
like image 139
TypeIA Avatar answered Oct 12 '22 23:10

TypeIA