在 C++14 中初始化 std::array 类型的静态 constexpr 成员

问题描述 投票:0回答:1

我无法以编程方式初始化静态 constexpr

std::array
成员。
这是我的问题的一个最小示例(因为简化尺寸已知并且很小,因此初始化可以是手动的,但我想将实际尺寸设为模板非类型参数,从而排除手动初始化):

#include <array>

constexpr std::size_t N = 3;
using Mat = std::array<double, N * N>;

// OK, can initialize a free constexpr Mat
constexpr Mat InitEye() noexcept {
    Mat TmpEye{0};
    for (std::size_t r = 0; r < N; ++r) {
        for (std::size_t c = 0; c < N; ++c) {
            TmpEye[r * N + c] = (r == c) ? 1. : 0.;
        }
    }
    return TmpEye;
}

// KO
class Wrapper {
   private:
    // KO cannot use it to initialize static constexpr member
    static constexpr Mat WrappedInitEye() noexcept {
        Mat TmpEye{0};
        for (std::size_t r = 0; r < N; ++r) {
            for (std::size_t c = 0; c < N; ++c) {
                TmpEye[r * N + c] = (r == c) ? 1. : 0.;
            }
        }
        return TmpEye;
    }

   public:
    static constexpr Mat Eye = WrappedInitEye();
};

// also KO
class Wrapper2 {
   public:
    // OK in C++17, still KO in C++17 due to lack of constexpr access operator
    static constexpr Mat Eye = [] {
        Mat TmpEye{0};
        for (std::size_t r = 0; r < N; ++r) {
            for (std::size_t c = 0; c < N; ++c) {
                TmpEye[r * N + c] = (r == c) ? 1. : 0.;
            }
        }
        return TmpEye;
    }();
};

int main() {
    constexpr Mat Eye = InitEye();
    constexpr Mat Eye2 = Wrapper::Eye;
    return 0;
}

我找到的最接近的答案是这个(因此是上面的 lambda 版本)。

但是现场示例 si 显示了两个问题:

  1. 非 lambda 版本永远无法工作:
\<source\>:32:46: error: 'static constexpr Mat Wrapper::WrappedInitEye()' called in a constant expression before its definition is complete
   32 |     static constexpr Mat Eye = WrappedInitEye();
  1. Lambda 版本在 C++14 中也不起作用,因为缺少
    constexpr
    std::array
  2. 访问功能

非 lambda 版本给出了这个 “不完整的定义” 错误,因为:

但是,如何使用 C++14 实现编程式

std::array
初始化?

c++ arrays static initialization constexpr
1个回答
3
投票
  1. 为什么非 lambda 版本会出现“不完整定义”错误?

因为静态成员函数

WrappedInitEye
的定义要到类定义结束时才算完成。我不太确定标准中的哪个位置解决了这个问题,但是如果您将
WrappedInitEye
设为自由函数,那么该特定错误就会消失。

  1. 如何使用 C++14 实现编程式
    std::array
    初始化?

我在 C++14 中能做的最好的事情是:

#include <array>
#include <utility>

constexpr std::size_t N = 3;
using Mat = std::array<double, N * N>;

template<std::size_t Index>
static constexpr bool condition = Index / N == Index % N;

template<std::size_t Index>
constexpr auto genElement(char(*)[condition<Index>] = 0) -> double {
    return 1.;
}

template<std::size_t Index>
constexpr auto genElement(char(*)[!condition<Index>] = 0) -> double {
    return 0.;
}

template<std::size_t... Indices>
constexpr auto gen(std::index_sequence<Indices...>) {
    return Mat{ genElement<Indices>()... };
}

int main() {
    constexpr Mat a = gen(std::make_index_sequence<N*N>());
}

演示

它仅适用于您的特定模式(单位矩阵,因此

Index / N == Index % N
时为 1,否则为 0),但您也许可以修改它以适应更复杂的模式。
char(*)[condition<Index>()] = 0
部分使用SFINAE。我从这里得到的。

您还可以使用标签调度:

template<std::size_t Index>
using Condition = std::integral_constant<bool, Index / N == Index % N>;

template<std::size_t Index>
constexpr auto genElement(std::true_type) -> double {
    return 1.;
}

template<std::size_t Index>
constexpr auto genElement(std::false_type) -> double {
    return 0.;
}

template<std::size_t... Indices>
constexpr auto gen(std::index_sequence<Indices...>) {
    return Mat{ genElement<Indices>(Condition<Indices>{})... };
}

演示

© www.soinside.com 2019 - 2024. All rights reserved.