【问题标题】:link error while compiling this "simple" program with clang and g++使用 clang 和 g++ 编译这个“简单”程序时出现链接错误
【发布时间】:2014-02-16 02:49:26
【问题描述】:

我正在做一个项目,clang 似乎无法生成有效的字节码(由于链接器无法链接,在链接时找不到模板类中的一些 static constexpr) 我可以在类中使用静态 getter 来修复它,但这会导致一些非常丑陋/重载的代码。

这是一个“最小”的代码示例,它使 bug(它是一个 bug 吗?)出现。 不幸的是,在这里,g++ 会产生同样的链接错误。

我在问这是否是编译器错误,或者我只是做错了什么,以及是否有解决方案可以避免此错误。 (如果我做错了什么,为什么 same 构造在我更大的项目中有效??)

注意:更大的项目在 github 上被命名为 yaggler,但仍处于生命的早期阶段

#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

【问题讨论】:

    标签: c++ c++11 g++ constexpr clang++


    【解决方案1】:

    vector 类模板的 2 和 3 模板参数的特化具有没有命名空间范围定义的文字类型的 static constexpr 数据成员 value(分别为 vector2vector3)。

    您将需要它们,因为您在将 value 传递给 do_something 函数时绑定到引用参数时使用 odr-use。

    §9.4.2/3 [class.static.mfct]

    如果非易失性const static 数据成员是整数或枚举类型,则其在类中的声明 定义可以指定一个brace-or-equal-initializer,其中每个initializer-clause(即assignment- expression)都是一个常量表达式(5.19)。文字类型的static 数据成员可以在 使用 constexpr 说明符定义类;如果是这样,其声明应指定brace-or-equal-initializer 其中每个initializer-clauseassignment-expression 是一个常量表达式。 [注:在这两个 在这些情况下,成员可能出现在常量表达式中。 —尾注] 成员仍应被定义 如果在程序中使用了 odr-used (3.2) 并且命名空间范围定义不应在命名空间范围内 包含一个初始化程序。

    编辑:纠正我自己,实际上是 some_wrapper&lt;T&gt;::value 需要这个定义(尽管如此)。所以你需要的是 some_wrapper 定义之后的命名空间范围内的 this:

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

    之后,您的代码compiles and runs

    【讨论】:

    • 哇。我不认为我必须为static constexpr 成员这样做。非常感谢完整的答案和解决方案! (这有效地解决了“更大的项目”中的链接器错误)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 2022-07-13
    • 1970-01-01
    • 2015-07-11
    相关资源
    最近更新 更多