【问题标题】:Strange C++ link error奇怪的 C++ 链接错误
【发布时间】:2016-07-09 08:23:02
【问题描述】:

当我尝试编译这个时,

#include <iostream>

struct K{
    const static int a = 5;
};

int main(){
    K k;

    std::cout << std::min(k.a, 7);
}

我得到关注。 gccclang 都给出了类似的错误:

/tmp/x-54e820.o: In function `main':
x.cc:(.text+0xa): undefined reference to `K::a'
clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)

如果我遵循,它编译没有问题。这和std::min的写法有关系吗?

#include <iostream>

struct K{
    const static int a = 5;
};

int main(){
    K k;

    std::cout << std::min((int) k.a, 7);  // <= here is the change!!!
}

另一种避免错误的方法是我自己做min():

template <class T>
T min(T const a, T const b){
    return a < b ? a : b;
}

类 C 预处理器 MIN 也可以正常工作。

【问题讨论】:

  • 为什么要标记 gcc 和 clang?你应该只使用一种编译器,它看起来是铿锵声
  • ideone.com/yPtq6w 为我工作
  • 尝试更改您的min 以通过引用获取参数。
  • 你的例子应该有#include &lt;algorithm&gt; 对应std::min
  • 从 C++11 开始,另一种解决方法是使用 min:std::min({k.a, 7}) 的列表形式。我认为这不是 odr-use 因为初始化列表按值复制。从 C++14 开始,列表形式甚至会产生 constexpr

标签: c++ gcc clang min


【解决方案1】:

这个问题经常被问到。 我相信这是clang中的一个错误。 a 太早被检测为常量表达式,编译器没有生成它的定义。(参见 cmets 中的更正)

std::min 通过 const 引用获取其参数,因此必须存在定义。

#include <iostream>

struct K{
    const static int a = 5;
};

int main(){
    K k;

    std::cout << std::min(k.a, 7);
}

这是一个可移植的workaround替代方案:

#include <iostream>

struct K{
    constexpr static int a() { return 5; }
};


int main(){
    K k;

    std::cout << std::min(k.a(), 7);
}

【讨论】:

  • IIRC 优化级别也可能会影响这一点。
  • @juzzlin 在没有优化的情况下无法链接到 Apple clang。编译器无法正确计算 a 的 ODR 使用。
  • @Richard Hodge
  • 不,这不是一个错误(我认为它也是......)。但是静态 const 整数初始化仍然不是根据标准的定义:9.4.2 静态数据成员 [class.static.data] §3 如果非易失性 const 静态数据成员是整数或枚举类型,则其类定义中的声明可以指定一个大括号或等号初始化器,其中作为赋值表达式的每个初始化器子句都是一个常量表达式... 如果该成员是 odr- used (3.2) ... 并且命名空间范围定义不应包含初始化程序。
  • @SergeBallesta 更正以发布回应。谢谢。
【解决方案2】:

std::min 通过引用接受参数。绑定对对象的引用意味着该对象是odr-used([basic.def.odr]/2 中有一个代码示例与您的示例几乎相同)。

但是在(int)k.a 的情况下,k.a 不是odr-used;因为它正在执行左值到右值的转换,这会产生一个常量表达式。 (这里也有其他一些条件,但是您的代码还可以)。

如果一个对象是odr-used,那么它必须只有一个定义;违反此规则无需诊断。所以第一种情况可能会也可能不会被接受;并且必须接受第二种情况。

在您自己的min 版本中,它按值接受参数,这类似于(int)k.a 的情况——对k.a 采取的唯一操作是右值转换来初始化min 的参数。

您可以在 C++ 标准草案的[basic.def.odr] 部分阅读有关 odr-use 的完整规则集。

【讨论】:

  • 缺少一行const int K::a;
  • @sp2danny 不一定;例如没有那条线,第二个样本是正确的
  • 顺便说一句,9.4.2 静态数据成员 [class.static.data] §3 特别明确地用于该用例 如果非易失性 const 静态数据成员是整数或枚举类型,它在类定义中的声明可以指定一个大括号或等式初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式... 如果该成员是odr-used (3.2) 在程序和命名空间范围定义中不得包含初始化器。
【解决方案3】:

您已经在结构中声明了一个静态变量 (a),但您还没有定义它。

struct K
{
    const static int a; // declaration
};

const int K::a = 5; // definition 


int main()
{
    std::cout << std::min(K::a, 7);
}

您可能会发现this link 很有帮助。

我也同意 Richard Hodges 的回答。

【讨论】:

  • 这行不通,原因与 OP 的尝试行不通。
  • 你是对的。我正在使用 g++ -std=c++11 -O2 -Wall 并且它有效。删除 -O2 使其不会。我要编辑答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多