8

我知道有很多类似的问题,但不知何故不同。这是关于以下情况:

#include <iostream>
#include <array>

template<typename T> class MyClass
{
public:
    static constexpr std::array<T,4> ARRAY {{4, 3, 1, 5}};
};

int main()
{
    constexpr std::array<int, 4> my_array(MyClass<int>::ARRAY); // works fine -> can use the ARRAY to initialize constexpr std::array

    constexpr int VALUE = 5*MyClass<int>::ARRAY[0]; // works also fine

    int value;
    value = my_array[0]; // can assign from constexpr
    value = MyClass<int>::ARRAY[0]; // undefined reference to `MyClass<int>::ARRAY

    std::cout << VALUE << std::endl;
    std::cout << value << std::endl;

    return 0;
}

据我了解constexpr是用于编译时常量。因此编译器已经可以进行一些计算,例如计算VALUE. 此外,我显然可以定义 a constexpr std::array<,>,从中我可以将值分配给运行时变量。我希望编译器已经设置value = 4到可执行程序中,以避免加载操作。但是,我不能直接从静态成员分配,得到错误

undefined reference to `MyClass<int>::ARRAY'
clang-3.7: error: linker command failed with exit code 1

这对我来说毫无意义,因为它可以通过另一个constexpr变量的中间步骤来完成。

所以我的问题是:为什么不能将类的静态 constexpr 成员分配给运行时变量?

注意:在我的 MWE 中,该类是一个模板类,不会影响错误。但是,我最初对这种特殊情况感兴趣,我希望它对非模板类更通用。

(编译器是clang++g++-std=c++11- 他们给出相同的错误)

编辑:@Bryan Chen:忘记了输出行。现已添加。

4

2 回答 2

7

undefined reference是一个链接器错误。规则是,如果一个变量是odr-used那么它必须有一个定义。这甚至适用于constexpr变量。

像大多数 ODR 规则一样,违反它是未定义的行为,不需要诊断(这可以解释为什么您没有看到对值的某些使用进行诊断)。

要修复错误,请在类外部添加一个定义:

template<typename T> constexpr std::array<T,4> MyClass<T>::ARRAY;

由于它是一个模板,因此您实际上可以将其放在标题中,而不是通常将定义放在一个.cpp文件中的情况。


这里的主要问题是是否ARRAY[0]算作odr-use。根据这篇详细的帖子,在 C++11 和 C++14 中,对数组进行索引确实算作odr-use,但这已被DR 1926针对 C++14 提交的文件更改为不属于odr-use

但是,那是在谈论内置数组。IDK是否同样的道理适用于std::array,我觉得[basic.def.odr]/3的文字很难理解。根据cppreference 上的非正式定义std::array::operator[]会导致数组的odr 使用,因为它的返回值绑定了对数组的引用。

于 2016-03-11T01:10:50.613 回答
4

出于这个原因,我总是从 constexpr 函数返回 constexpr 对象。

修改后的代码如下。请注意,由于 c++14 的缺陷,std::array<>您必须返回 aconst std::array才能允许operator[]工作。

#include <iostream>

#include <iostream>
#include <array>

template<typename T> class MyClass
{
public:
    static constexpr const std::array<T,4> ARRAY() { return {4, 3, 1, 5}; };
};

int main()
{
    constexpr std::array<int, 4> my_array(MyClass<int>::ARRAY()); // works fine -> can use the ARRAY to initialize constexpr std::array

    constexpr int VALUE = 5 * MyClass<int>::ARRAY()[0]; // works also fine

    int value;
    value = my_array[0]; // can assign from constexpr
    value = MyClass<int>::ARRAY()[0]; // undefined reference to `MyClass<int>::ARRAY

    std::cout << VALUE << std::endl;
    std::cout << value << std::endl;

    return 0;
}

预期成绩:

20
4
于 2016-03-10T22:06:23.593 回答