Ошибка компоновщика G ++ (C ++ 14) при работе кода C ++ 03 - PullRequest
0 голосов
/ 24 мая 2018

Рассмотрим следующий фрагмент кода.

class aClass
{
public:
    static const int HALLO = -3;
};

int main()
{
  std::vector<double > a;
  std::vector<int> b;
  std::vector<int> c;
  int d = aClass::HALLO; //fine
  a.resize(10,aClass::HALLO); //fine
  b.resize(10,aClass::HALLO); // linker error c++11 and c++14
  c.resize(10,(int)(double)aClass::HALLO); //fine
  std::cout<<a[0]<<endl;
  std::cout<<b[0]<<endl;
  std::cout<<c[0]<<endl;
  return 0;
}

Компиляция работает с C ++ 03 и дает вывод:

-3
-3
-3

Компиляция с C ++ 11 или C ++14, однако, приводит к ошибке компоновщика:

/tmp/cc3BARzY.o: In Funktion `main':
main.cpp:(.text+0x66): Nicht definierter Verweis auf `aClass::HALLO'
collect2: error: ld returned 1 exit status

Как ни странно, это происходит только для вектора b.Если есть приведение к удвоению (a) или даже удвоению и возвращению к int (c), код выполняется так, как ожидалось.

Как это поведение можно объяснить?

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Причина, по которой это работает для double вектора, но не int, забавна.Подпись для std::vector::resize является void resize(size_type count, const value_type& value ) начиная с C ++ 11.Взятие ссылки на объект делает его ODR-используемым, и поэтому ваш статический член int теперь должен быть определен где-то в вашем приложении.

Однако, когда вы std::vector<double>, вы можете 'связать ссылку на объект.Вместо этого компилятор создает временный объект double и привязывает ссылку к указанному временному объекту.Из-за этого вы избегаете использования ODR статического члена класса, так как создание временного double не использует ODR, а временное использование ODR это нормально.

Исправление проблемы - тривиальноесли у вас есть файл .cpp для класса, в этом случае вы просто определяете свою статичность там.Однако для класса только с заголовком решение не является тривиальным до C ++ 17, где вы можете иметь встроенные переменные и иметь очень хорошее решение:

#include <vector>

class aClass
{
public:
    static const int HALLO;
};

inline const int aClass::HALLO = -3;

int main()
{
  std::vector<int> b;
  b.resize(10,aClass::HALLO); //fine

  return 0;
}
0 голосов
/ 24 мая 2018

До C ++ 11 подпись std::vector::resize() была

void resize( size_type count, T value = T() );

Теперь вместо

void resize( size_type count, const value_type& value );

изменение от передачи к значению к передаче-const-ref приводит к тому, что ODR используется для вызова aClass::HALLO там, где раньше этого не было.Приведение к double и обратно к int дает временный способ, который позволяет избежать использования ODR;вызов a.resize() работает по той же причине, поскольку значение int неявно приведено к double, а ссылка на аргумент привязана к полученному временному.

Обычное исправление здесь - дать определение для aClass::HALLO;если по какой-то причине это нежелательно для вас, сокращение для временного использования ODR означает применение одинарного operator+:

b.resize(10, +aClass::HALLO);
...