将静态constexpr类成员分配给运行时变量

mar*_*lam 8 c++ templates one-definition-rule constexpr c++11

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

undefined reference to `MyClass<int>::ARRAY'
clang-3.7: error: linker command failed with exit code 1
Run Code Online (Sandbox Code Playgroud)

这对我没有意义,因为它可以通过另一个constexpr变量的中间步骤来完成.

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

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

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

编辑:@Bryan Chen:忘了输出线.现在加入.

M.M*_*M.M 7

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

与大多数ODR规则一样,违反它是未定义的行为,无需诊断(这可以解释为什么您没有看到对该值的某些用途的诊断).

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

template<typename T> constexpr std::array<T,4> MyClass<T>::ARRAY;
Run Code Online (Sandbox Code Playgroud)

因为它是一个模板,你实际上可以把它放在标题中,而不是通常情况下定义恰好在一个.cpp文件中.


这里的主要问题是ARRAY[0]计算是否使用.根据这篇详细的文章,在C++ 11和C++ 14中,对数组进行索引确实算作odr-use,但是对于C++ 14提交的DR 1926改变了这种情况.

然而,这是在讨论内置数组.IDK是否适用相同的理由std::array,我发现[basic.def.odr]/3的文字难以理解.根据cppreference非正式定义,std::array::operator[]会导致数组使用odr,因为它的返回值绑定了对数组的引用.