模板元编程静态成员函数无法获取 class 的 const 静态值

template metaprogramming static member function cannot get const static value of class

我正在尝试在编译时生成数字并尝试使用模板。但是当我使用 constexpr static 成员变量而不是 enum 时,在 static 成员函数中我试图将它推入 std::vector,编译器告诉我 linker 无法 link.

例如,这里有一个计算 n.

的阶乘的简单程序
#include <iostream>
#include <vector>

template <uint64_t n> struct factorial {
    constexpr static uint64_t value = factorial<n - 1>::value * n;
    static void put(std::vector<uint64_t> &v) {
        factorial<n - 1>::put(v);
        v.push_back(value);
    }
};

template <> struct factorial<0> {
    constexpr static uint64_t value = 1;
    static void put(std::vector<uint64_t> &v) {
        v.push_back(1);
    }
};

int main() {
    using namespace std;
    vector<uint64_t> v;
    factorial<10>::put(v);
    for (auto fact: v)
        cout << fact << endl;
    return 0;
}

这将在 g++ 7.1 和 clang 4.0 中产生 link 失败信息,所以我认为这不是错误。当我将 constexpr static 更改为 enum 时,例如

template <uint64_t n> struct factorial {
    enum { value = factorial<n - 1>::value * n };
    static void put(std::vector<uint64_t> &v) {
        factorial<n - 1>::put(v);
        v.push_back(value);
    }
};

template <> struct factorial<0> {
    enum { value = 1 };
    static void put(std::vector<uint64_t> &v) {
        v.push_back(1);
    }
};

它编译 link 并且运行良好。

我想知道 C++ 标准是否提到了这一点。

尝试(在您的第一个示例中)添加

template <uint64_t n>
constexpr uint64_t factorial<n>::value;

据我所知这是因为 std::vector<T>::push_back() 有签名
void push_back(V const&).

因此参考被认为是有价值的。

因此它必须有一个地址,但它没有,因为它从未被定义(虽然这对我来说似乎有点不合逻辑)——也许这在 c++17 中得到了修复?

可以通过复制并推送来编译:

#include <iostream>
#include <vector>

template <uint64_t n> struct factorial {
    constexpr static uint64_t value = factorial<n - 1>::value * n;
    static void put(std::vector<uint64_t> &v) {
        factorial<n - 1>::put(v);
        auto vcpy = value;   // *** HERE ***
        v.push_back(vcpy);
    }
};

template <> struct factorial<0> {
    constexpr static uint64_t value = 1;
    static void put(std::vector<uint64_t> &v) {
        v.push_back(1);
    }
};

int main() {
    using namespace std;
    vector<uint64_t> v;
    factorial<10>::put(v);
    for (auto fact: v)
        cout << fact << endl;
    return 0;
}