Я работаю над проектом, и кажется, что clang не может сгенерировать действительный байт-код (поскольку компоновщик не может установить связь, некоторые static constexpr в классе шаблона не найдены во время связывания) Я могу исправить это с помощью статического получателя в классе, но это приведет к действительно уродливому / перегруженному коду.

Вот «минимальный» образец кода, из-за которого появляется ошибка (это ошибка?). К сожалению, здесь g ++ выдаст ту же ошибку ссылки.

Я спрашиваю, ошибка ли это компилятора или я просто делаю что-то не так, и есть ли решение, позволяющее избежать этой ошибки. (И если я делаю что-то не так, почему такая же конструкция работает в моем более крупном проекте ??)

ПРИМЕЧАНИЕ : более крупный проект называется yaggler на github, но все еще находится в самом начале своей жизни

#include <type_traits>
#include <iostream>

// exemple vector classes
struct vector2
{
  constexpr vector2(unsigned int _x = 0, unsigned int _y = 0) : x(_x), y(_y) {} // for clang

  unsigned int x;
  unsigned int y;
};
struct vector3 : public vector2 // uh ;)
{
  constexpr vector3(unsigned int _x = 0, unsigned int _y = 0, unsigned int _z = 0) : vector2(_x, _y), z(_z) {} // for clang
  unsigned int z;
};

// simple templated generic vector type
// we could make a more generic one, but this would require something like a tuple.
template<unsigned int... Vals>
struct vector
{
    static_assert(!(sizeof...(Vals) + 1), "[...]");
};
template<unsigned int X>
struct vector<X>
{
  using vec_type = unsigned int;
  static constexpr unsigned int value = X;
};
template<unsigned int X, unsigned int Y>
struct vector<X, Y>
{
  using vec_type = vector2;
  static constexpr vector2 value = vector2(X, Y);
};
template<unsigned int X, unsigned int Y, unsigned int Z>
struct vector<X, Y, Z>
{
  using vec_type = vector3;
  static constexpr vector3 value = vector3(X, Y, Z);
};

// a simple wrapper
template<typename V>
struct some_wrapper
{
  static constexpr typename V::vec_type value = V::value;
};

// a dummy function that print something to stdout.
void do_something(int32_t id, const vector3 &value)
{
  std::cout << id << " " << value.z << std::endl;
}
void do_something(int32_t id, const vector2 &value)
{
  std::cout << id << " " << value.y << std::endl;
}
void do_something(int32_t id, int value)
{
  std::cout << id << " " << value << std::endl;
}

// the class used to create the error
template< typename... Args>
class exemple
{
  private:
    // an initialisation that recurse over the Args... template arguments
    template<typename Current, typename... Others>
    void __rec_init() const
    {
      do_something(0, Current::value);
      __rec_init<Others...>();
    }

    // end of recursion
    template<size_t = 0>
    void __rec_init() const {}

    // launch the recursion
    void tpl_init() const
    {
      __rec_init<Args...>();
    }

  public:
    exemple()
    {
      tpl_init();
    }
};

int main()
{
  // and here, we get a linker error.
  exemple<some_wrapper<vector<4, 4, 5>>, some_wrapper<vector<4, 1>>, some_wrapper<vector<9>>>();
}

РЕДАКТИРОВАТЬ : просто упомянуть версии gcc и clang: gcc 4.7.3 / 4.8.2 и clang 3.2 / 3.3

2
neam 25 Янв 2014 в 02:58

1 ответ

Лучший ответ

Специализации шаблона класса vector для 2 и 3 аргументов шаблона имеют static constexpr элемент данных value литерального типа (vector2 и vector3 соответственно), которые не нет определений области пространства имен.

Они вам понадобятся, потому что вы odr-use value, когда он привязывается к параметру ссылки при передаче в функцию do_something.

§9.4.2 / 3 [class.static.mfct]

Если энергонезависимый член данных const static имеет целочисленный или перечисляемый тип, его объявление в классе определение может указывать brace-or-equal-initializer, в котором каждый initializer-clause, являющийся назначением - выражение - постоянное выражение (5.19). Член данных static литерального типа может быть объявлен в определение класса со спецификатором constexpr; в таком случае в его декларации должен быть указан brace-or-equal-initializer в котором каждый initializer-clause, являющийся assignment-expression, является постоянным выражением. [Примечание: в обоих В этих случаях член может появляться в постоянных выражениях. —В конце примечания] Член все еще должен быть определен в области пространства имен, если она используется odr (3.2) в программе, и определение области пространства имен не должно содержат инициализатор.

РЕДАКТИРОВАТЬ: Исправляю себя, это действительно some_wrapper<T>::value, которому нужно это определение (тем не менее, по указанной выше причине). Итак, что вам нужно, так это это в области пространства имен после определения some_wrapper:

template<typename V>
constexpr typename V::vec_type some_wrapper<V>::value;

После этого ваш код компилируется и запускается .

2
jrok 25 Янв 2014 в 03:25
Ух ты. Я не думал, что должен делать это для участников static constexpr. Большое спасибо за исчерпывающий ответ и решение !! (это эффективно устраняет ошибку компоновщика в "большом проекте" )
 – 
neam
25 Янв 2014 в 03:31