用另一个数组的值初始化一个本地静态常量数组

Posted

技术标签:

【中文标题】用另一个数组的值初始化一个本地静态常量数组【英文标题】:Initialize a local static const array with values of another array 【发布时间】:2016-05-10 08:08:51 【问题描述】:

假设我想声明一个 local 静态常量数组,但我不知道它在编译时的初始化值,必须先计算这些值。 例如,我有一个数组int p[100]。我写了一个循环来用前 100 个素数填充它。在计算完这些之后,我定义了一个static const int primes[100],它必须由p 的值初始化。我该怎么做?

附: “为什么我要声明一个本地静态 const 对象?”的问题?可能没有令人满意的答案,但我认为这里不是这样。

附言我提到质数只是作为一个例子。这不是重点。

P.S.S.S.假设p 有 1000000 个成员。那么 user2079303 建议的解决方案肯定值得更多的支持。

【问题讨论】:

@EddeAlmeida - 这根本不是真的。常量可以在运行时初始化,只是不能改变。考虑函数的局部常量 好的@Smeeheey。我删除了我之前发表的愚蠢评论。 那么我应该说,常量在创建时就被初始化了,在那之后就不能再改变了。因此,不能只创建一个常量来稍后给它一个值。这是正确的吗? 如果你真的需要一个不是真正 const 的静态 const 数组,我会使用 memcpyconst_cast 的组合。 您能否提供更多详细信息? @KarstenKoop 【参考方案1】:

可以在运行时初始化一个静态常量数组,但是有点繁琐:

int* init = get_run_time_array();
static const int primes[100] 
        init[0], init[1], init[1], init[2], init[3], init[4], init[5], // ...
;

【讨论】:

【参考方案2】:

为了回避您的问题,在编译时使用模板元编程计算您的素数实际上是完全可行的。下面的代码显示了一种可能的方法。主函数创建实例化 PrimeGen 类型以生成第一个 100 个素数的编译时 std::array。很抱歉代码有点难以阅读,因为这里有很多模板样板。基本上大部分逻辑都发生在第一个 PrimeGen 特化中,它使用 Eratosthenes 筛生成素数,使用以下基本公式:

    从 N=100(需要的数字尚未生成)开始,Next = 2,一个空的素数列表和一个空的过滤器列表

    检查 Next 是否为素数(即,通过所有现有过滤器)。如果是这样,请将其添加到素数列表中,递减 N 并添加一个新过滤器,该过滤器将无法通过任何可被 Next 整除的数字。

    使用上述修改后的参数以及 Next 递增到 Next+1,递归到 PrimeGen 的下一个特化 当 N=0 时终止

显然这个程序的编译时间会比较长,因为编译器计算素数。

    #include <iostream>
    #include <array>

    template <size_t Mod>
    struct ModFilter
    
        template<size_t N>
        static constexpr bool apply()
        
            return N % Mod != 0;
        
    ;

    template <size_t N, typename ... Filters>
    struct CheckFilters;

    template <size_t N>
    struct CheckFilters<N>
    
        static const bool pass = true;
    ;

    template <size_t N, typename Filter, typename ... Filters>
    struct CheckFilters<N, Filter, Filters...>
    
        static const bool pass = Filter::template apply<N>() && CheckFilters<N, Filters...>::pass;
    ;

    template<typename ... Filters>
    struct FilterPack;

    template<size_t ... Numbers>
    struct NumberList;

    template <size_t N, bool doAppend, typename Numbers>
    struct ConditionalAppend
    
        typedef Numbers Output;
    ;

    template <size_t N, size_t ... Numbers>
    struct ConditionalAppend<N, true, NumberList<Numbers...>>
    
        typedef NumberList<Numbers..., N> Output;
    ;

    template <size_t N, bool doAppend, typename Filters>
    struct ConditionalAppendFilter
    
        typedef Filters Output;
    ;

    template <size_t N, typename ... Filters>
    struct ConditionalAppendFilter<N, true, FilterPack<Filters...>>
    
        typedef FilterPack<Filters..., ModFilter<N>> Output;
    ;

    template<size_t N, size_t Next, typename Numbers, typename Filters>
    struct PrimeGen;

    template<size_t N, size_t Next, size_t ... Numbers, typename ... Filters>
    struct PrimeGen<N, Next, NumberList<Numbers...>, FilterPack<Filters...>>
    
        static constexpr std::array<size_t, N + sizeof...(Numbers)> numbers
            = PrimeGen<CheckFilters<Next, Filters...>::pass ? N-1 : N,
                        Next+1,
                        typename ConditionalAppend<Next, CheckFilters<Next, Filters...>::pass, NumberList<Numbers...>>::Output,
                        typename ConditionalAppendFilter<Next, CheckFilters<Next, Filters...>::pass, FilterPack<Filters...>>::Output>
              ::numbers;

        static const int size = N + sizeof...(Numbers);
    ;

    template<size_t Next, size_t ... Numbers, typename ... Filters>
    struct PrimeGen<0, Next, NumberList<Numbers...>, FilterPack<Filters...>>
    
        static constexpr std::array<size_t, sizeof...(Numbers)> numbers = Numbers...;
        static const int size = sizeof...(Numbers);
    ;

    template<size_t N, size_t Next, size_t ... Numbers, typename ... Filters>
    constexpr std::array<size_t,N + sizeof...(Numbers)> PrimeGen<N, Next, NumberList<Numbers...>, FilterPack<Filters...>>::numbers;

    template<size_t Next, size_t ... Numbers, typename ... Filters>
    constexpr std::array<size_t,sizeof...(Numbers)> PrimeGen<0,Next,NumberList<Numbers...>,FilterPack<Filters...>>::numbers;

    int main()
    
        using MyGen = PrimeGen<100, 2, NumberList<>, FilterPack<> >;

        for(int i=0; i<MyGen::size; ++i)
            std::cout << MyGen::numbers[i] << std::endl;

        return 0;
    

【讨论】:

虽然我以素数为例,但这不是重点,但您的解决方案似乎是合法的。 +1【参考方案3】:

最有效的解决方案是:

static const int primes[100] =  2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541 ;

您现在甚至可以取消整个 p 计算。

【讨论】:

不。既然已经知道结果,为什么还要浪费时间进行计算?【参考方案4】:

我刚刚阅读了this great answer 到 SO 中的另一个问题并想通了。无需将局部变量声明为static const。似乎只有static 足以使计算值保持在范围之外。

【讨论】:

为什么? @LightnessRacesinOrbit 这里没有足够的细节可以说,但从你告诉我们的一点点来看,这听起来像是一种误解。 在评论中,我说:例如,假设我计算p 的值,然后写someptr=&amp;p[0],其中someptr 是全局int*。如果我退出本地范围,计算值将被破坏。但是如果我首先将它们分配给一个静态常量数组,然后写入someptr=&amp;primes[0],它们将在本地范围之外保持不变。我希望这能澄清这个问题的原因,我真的看不出有什么误解。 @LightnessRacesinOrbit 嗯,好的。在 this 答案中,您似乎建议 static 授予不变性。它没有。我想我不明白 const 与任何事情有什么关系。 我同意。 const 这个词可能会引起一些误解。乍一看,我希望它们在本地范围内保持不变并且在本地范围外可访问。看来我们一般不能两者兼得。 @LightnessRacesinOrbit

以上是关于用另一个数组的值初始化一个本地静态常量数组的主要内容,如果未能解决你的问题,请参考以下文章

C# 与 C++ 静态数组中静态常量列表初始化的效率

如何初始化一个静态数组?

转换为常量类型,初始化数组

如何从 C++ 中的 char 指针初始化 char 数组?

用另一种方法访问数组

数组-静态初始化和动态初始化