Ошибка ссылки при использовании std :: make_unique при создании объекта с помощью const char * - PullRequest
0 голосов
/ 10 января 2019

Извинения за то, что не предоставили простой исполняемый код ошибки. Ошибка является частью большей кодовой базы, которая потребует большого количества рефакторинга.

У меня очень странная проблема с ссылками на код, которую я пока не могу решить. У меня есть класс с static constexpr const char * для некоторых строк и локальный std::sunique_ptr. Указатель на другой шаблонный класс, который содержит другой шаблонный класс (# 2).

Основной класс такой (сокращенно):

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_;
};

Таким образом, конструктор DataReader принимает два параметра const string &.

Если я объявлю faults_ обычным старым указателем и создам его с помощью new, код запускается и ссылки просто отлично: DataReader *faults_ = new DataReader<uint32_t>(kActuatorsCommand, kActuatorsProxy).

Однако, если я использую std::make_unique, компоновщик жалуется, что существует неопределенная ссылка на эти строки static const char*, даже если они находятся в заголовке класса.

Кроме того, если я удалю класс # 2, все будет нормально.

Использование gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0


Я понимаю, что это может быть довольно расплывчатый вопрос, но какое-то направление, куда смотреть, будет оценено.

Кроме того, этот вопрос может быть похож на этот . Однако в моем случае все находится на одном двоичном файле.


Обновление: наконец-то нашли, как его воспроизвести.

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() {}

Код не связывается при компиляции с -o0. С -03 он нормально связывается.

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

1 Ответ

0 голосов
/ 10 января 2019

Я думаю, что это связано с встраиванием

Спецификатор constexpr, используемый в функции или статической переменной-члене (начиная с C ++ 17) объявление подразумевает inline

Когда вы компилируете код с помощью -O3 или -O0 -std=c++17, статическая переменная может быть встроенной, что позволяет избежать неопределенной ссылки.

Я немного покопался и нашел:

Вы можете взять адрес статического члена, если (и только если) он имеет внеклассное определение

Из FAQ Бьярна Страуструпа по C ++ . Добавление следующего кода в ваш пример вне тела класса привело к его компиляции с использованием -std=c++14:

constexpr const char* Manager::kActuatorsCommand;
constexpr const char* Manager::kActuatorsProxy;
...