使用静态const初始化unique_ptr时未定义的引用错误

esw*_*s13 8 c++ smart-pointers c++14

当我尝试使用a static const来初始化a时unique_ptr,我得到一个"未定义的引用"错误.但是,当我new使用相同的常量指针时,符号似乎是神奇地定义的.

这是一个重现错误的简单程序:

Outside_library.h

class Outside_library
{
public:
  static const int my_const = 100;
};
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

我使用编译程序

g++ main.cpp -std=c++14
Run Code Online (Sandbox Code Playgroud)

并得到以下错误.

/tmp/ccpJSQJS.o: In function `main':
main.cpp:(.text+0x51): undefined reference to `Outside_library::my_const'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

有人可以帮我理解为什么在使用new时定义了常量,但在使用时却没有make_unique

Typ*_*eIA 4

C++ 有一种称为“单一定义规则”(ODR) 的规则

通俗地讲,如果一个对象的值被读取(除非它是编译时常量)或写入,它的地址被获取,或者引用绑定到它,则该对象是 odr-used 的;如果使用了引用并且其所指对象在编译时未知,则该引用是 odr-used 的;如果对某个函数进行了函数调用或者获取了它的地址,则该函数就是 odr-used。如果对象、引用或函数是 odr 使用的,则其定义必须存在于程序中的某个位置;违反这一点通常是链接时错误。

链接站点给出了以下示例:

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
Run Code Online (Sandbox Code Playgroud)

您的显式构造函数调用不会“odr-use”,Outside_library::my_const但调用std::make_unique()会“odr-use”。当一个对象被 odr 使用时,它必须只有一个定义(而不是声明)。你的例子只有一个声明。再次来自cppreference

  1. 潜在求值表达式 ex中的变量 x是 odr 使用的,除非以下两个条件都成立:

    • 将左值到右值转换应用于 x 会产生一个不调用非平凡函数的常量表达式
    • x 不是对象(即 x 是引用),或者如果 x 是对象,则它是较大表达式 e 的潜在结果之一,其中该较大表达式要么是丢弃值表达式,要么具有对其应用左值到右值转换

Jarod42 建议的解决方案是使用constexpr而不是const(如果您可以控制“外部库”代码)。如果不这样做,那么您需要将程序链接到包含 的定义的库Outside_library::my_const

g++ main.cpp -std=c++14 -lOutside_library
Run Code Online (Sandbox Code Playgroud)

  • @Jarod42我不知道`constexpr`可以被odr使用,但我猜是这样的:/sf/ask/1833726681/而gcc只是不这样做诊断它。有趣的。 (2认同)