Clang не может создать конструктор перемещения по умолчанию при создании шаблона - PullRequest
0 голосов
/ 13 ноября 2018

Следующий код (я не смог сделать более короткий MVCE)

unit.h

#include <vector>

template<typename T>
struct foo
{
    std::vector<T> data;
    foo(foo&&) = default;         // no assembly generated
    foo(std::vector<T>&&v) : data(std::move(v)) {}
};

extern template struct foo<int>;  // indicates template instantiation elsewhere

unit.cc

#include "unit.h"
template struct foo<int>;         // forces template intantiation 

main.cc

#include "unit.h"

struct bar
{
    foo<int> f;
    bar(foo<int>&&x) : f(std::move(x)) {}
};

bar makeBar(int x)
{
    std::vector<int> v(x);
    foo<int> f(std::move(v));
    return {std::move(f)};
}

int main()
{
    bar x = makeBar(5);
}

не удается скомпилировать под clang (Apple LLVM версия 9.0.0 (clang-900.0.39.2) - какая версия llvm это? ) с результатом:

test> clang++ -std=c++11 -c unit.cc
test> clang++ -std=c++11 -c main.cc
test> clang++ -std=c++11 main.o unit.o
Undefined symbols for architecture x86_64:
  "foo<int>::foo(foo<int>&&)", referenced from:
      bar::bar(foo<int>&&) in main-476e7b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Все отлично работает с gcc (8.2.0). При проверке выясняется, что gcc испускает foo<int>::foo(foo<int>&&) в main.o, в то время как clang не может его полностью выдать.

Каково правильное поведение: конструктор перемещения default должен передаваться с unit.o или main.o? Это известная ошибка лязга?

полезная ссылка: https://en.cppreference.com/w/cpp/language/class_template

1 Ответ

0 голосов
/ 13 ноября 2018

Это ошибка лягушки.Ваш код правильно сформирован, поэтому, какой бы ни была стратегия компилятора, учитывающая правило «как будто», ваш код должен компилироваться.

Явное создание экземпляра шаблона класса только для экземпляров, для которых предоставляется определение [temp.explicit] / 9 :

Явное определение экземпляра, которое именует специализацию шаблона класса, явно создает экземпляр специализации шаблона класса и является явным определением экземпляра только тех членов, которые былиопределяется в момент создания.

Специальная функция-член, по умолчанию установленная в их первом объявлении, определяется только тогда, когда odr-used .Поэтому я предполагаю, что ошибка заключается в том, что Clang ожидает, что в момент явного создания экземпляра конструктор по умолчанию также был создан.

Таким образом, сначала можно обойтись, объявив конструктор перемещения в заголовочном файле, затемчтобы определить его как значение по умолчанию в файле реализации:

unit.hpp :

template<typename T>
struct foo
  {
  std::vector<T> data;
  foo(foo&&)=default;
  foo(std::vector<T>&&v) : data(std::move(v)) {}
  };
template<T>
foo<T>::foo(foo&&) noexcept; 
extern template struct foo<int>; 

unit.cpp :

#include <unit.hpp>

template<T>
foo<T>::foo(foo&&) noexcept = default;

template struct foo<int>; //foo(foo&&) has a definition so it is instantiated with the class.

Это приведет к генерации определения конструктора перемещения по умолчанию (см. [dlc.fct.def.default] / 5 ).Недостатком является то, что определение foo(foo&&) больше не является встроенным.


В качестве альтернативы решение ниже будет работать:

template<typename T>
struct foo
  {
  std::vector<T> data;
  foo(foo&& o)noexcept:data{move(o.data)}{};
  foo(std::vector<T>&&v) : data(std::move(v)) {}
  };
...