【问题标题】:C++ initialize/fill static member std::array elements to specific valueC++ 初始化/填充静态成员 std::array 元素到特定值
【发布时间】:2019-06-20 00:11:23
【问题描述】:

我想知道是否有合适的方法来做到这一点。

举个例子:

struct Test {
    static std::array<unsigned, 123> data;
};

std::array<unsigned, 123> Test::data = {};

如果我想将 data 中的所有元素设置为某个值,例如 5。

我可以这样做:

struct Test {
    static std::array<unsigned, 123> data;
private:
    static decltype(data) init_data_arr() {
        decltype(data) arr = {};
        arr.fill(5);
        return arr;
    }
};

std::array<unsigned, 123> Test::data = Test::init_data_arr();

但是我会分配一个额外的内存数组并对其进行复制,这似乎并不理想。

另一个不太占用内存的选项是这样的:

struct Test {
    static bool is_init;
    static std::array<unsigned, 123> data;

    Test() {
        if (!is_init) {
            is_init = true;
            data.fill(5);
        }
    }
};

bool Test::is_init = false;
std::array<unsigned, 123> Test::data = {};

但是现在我在构建我的 Test 结构时有额外的开销。

现在我知道您可以像这样初始化 pod 数组:std::array&lt;int, 3&gt; a = {1, 2, 3}; 但对于 N 大小的数组,这很快就会变得难以维护。

我似乎遇到的每个解决方案都只是在数组类上使用 fill 成员函数,但总是将数组视为非静态成员变量,或者它在 main 中初始化它们,这两者都不是真的帮助我的情况。

编辑: 我创建了这个实用程序头来概括第二个选项。 #pragma 一次

#include <utility>
#include <iterator>
#include <algorithm>
#include <type_traits>

namespace Utils {

    template<typename Container>
    using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<Container&>()))>;

    template<class Container>
    static Container create_filled_container(const element_type_t<Container>& value) {
        Container container = {};
        std::fill(container.begin(), container.end(), std::move(value));
        return container;
    }

}

这可能会得到更多改进,但它似乎有效。用法如下:

std::array<unsigned, 123> Test::data = Utils::create_filled_container<decltype(data)>(456);

它将所有元素设置为您指定的任何内容。

编辑2: 所以我做了一些更多的测试,看起来调用一个函数来填充数组与手动执行它并不总是产生相同的程序集。

我已经设置了一个演示 here!

编辑3: 改变了一些东西,所以它可能是 constexpr,这是它的当前形式:

template<class Container>
static constexpr Container create_filled_container(const element_type_t<Container>& value) {
    Container container = {};

    for (auto it = container.begin(); it != container.end(); ++it)
        *it = value;

    return container;
}

现在生成相同的程序集。

【问题讨论】:

  • 但是我会分配一个额外的内存数组并对其进行复制,这似乎并不理想。 你确定吗? NRVO 将消除这一点,让您无需支付任何额外费用。您应该检查程序集,看看它是否这样做。
  • @NathanOliver 好电话,我在测试程序集生成时忘记打开优化。但你是对的,当 -O3 用于 Test::init_data_arr() 示例与 {0, ..., n} 初始化时,它确实会产生相同的程序集。
  • @NathanOliver 那么你会说这可能是解决这个问题的最佳方式,还是有更优雅的解决方案?
  • 我会这样做。它易于理解,易于维护,并且应该编译掉。代码完成后,如果您对性能不满意,请查看配置文件并查看问题所在。很可能不会是这样。
  • 只需使用 constexpr 函数对其进行初始化。没有运行时代码。

标签: c++ arrays static-members array-initialization


【解决方案1】:

使用 constexpr 函数或 lambda 对其进行初始化。不生成运行时代码。

#include <array>
#include <iostream>

template <unsigned len, unsigned val>
constexpr std::array<unsigned, len> initialize_array()
{
    std::array<unsigned, len> ret{};
    for (int i = 0; i < len; i++)
        ret[i] = val;
    return ret;
}

struct Test {
    constexpr static std::array<unsigned, 123> data{ initialize_array<123,5>() };
};

int main() {
    std::cout << Test::data[4] << "\n";
}

【讨论】:

  • OP 的 data 甚至不是 const。 (可能只是初始值,因为 5 的 const 数组几乎没用)。
  • @Jarod42 是的,对于非 const 内存,需要内联数组,这对于 constexpr 是隐含的。不幸的是,在这两种情况下都没有指定编译器是否生成初始化数据。但它可以。通常,这些类型的初始化最好使用查找 const 表。我发现它们在诸如用于纠错的伽罗瓦算术等方面很有帮助和效率,并且已经看到了令人印象深刻的优化。随着时间的推移,C++ 编译器已经变得更好,而且很可能会继续下去。
  • @doug "在这两种情况下都没有指定编译器是否生成初始化数据" 我实际上并没有意识到这一点,我以为它被指定在静态初始化之前发生,但我猜你是对,他们只是说它必须“好像”在静态初始化之前发生。 en.cppreference.com/w/cpp/language/constant_initialization
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多