奇怪的 C++ link 错误

Strange C++ link error

当我尝试编译这个时,

#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 也可以正常工作。

这个问题经常被问到。 我认为这是 clang 中的一个错误。 a 被过早检测为常量表达式,编译器未生成它的定义。(请参阅评论中的更正)

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

#include <iostream>

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

int main(){
    K k;

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

这是一个可移植的解决方法替代方法:

#include <iostream>

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


int main(){
    K k;

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

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 的完整规则集。

您已经在您的结构中声明了一个静态变量 (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 的回答。