gxqcn 发表于 2018-10-9 10:12:18

如何优雅地获取常量数列中某个值?

问题是这样:

想获取 \(6\sigma\) 中的某一个值,在 C++11 之前是这么做的:#define _1_SIGMA_                   0.3085375
#define _2_SIGMA_                   0.6914625
#define _3_SIGMA_                   0.9331928
#define _4_SIGMA_                   0.9937903
#define _5_SIGMA_                   0.9997674
#define _6_SIGMA_                   0.9999966

#define SIGMA( n )                  ( _##n##_SIGMA_ )

而如今,采用 C++17 标准后,我们可以这么写了:inline constexpr double SIGMA( std::size_t n ) noexcept
{
    constexpr double sigma[]{ 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 };

    return sigma[ n ];
}

其实上面那个数组 sigma 是没有必要的(甚至无需给它命名),可以改写成如下:inline constexpr auto SIGMA( std::size_t n ) noexcept
{
    using D7 = double[ 7 ];

    return D7{ 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 }[ n ];
}

以上皆能编译通过。



但是,我觉得仍不够“优雅”,觉得理想的代码应该是下面的:inline constexpr auto SIGMA( std::size_t n ) noexcept
{
    return { 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 }[ n ];
}不过,很可惜,无 法 通 过 编 译 !



如果是你,用 C++ 会如何实现它?并尽可能保持优雅。。。

gxqcn 发表于 2018-10-9 10:31:26

刚刚同事给出一个方案,还算比较“优雅”:inline constexpr auto SIGMA( std::size_t n ) noexcept
{
    return std::begin( { 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 } )[ n ];
}能够编译通过。

用到了 std::initializer_list 的非成员函数 std::begin(std::initializer_list)

如果项目中尚未 include 任何类型的标准库容器,请在文件前加上“#include <initializer_list>”

zhouguang 发表于 2018-10-9 13:55:26

我或许会用const float SIGMA[]={...};,或许不优雅,哈哈。

gxqcn 发表于 2018-10-9 16:21:00

这里的 constexpr 的作用是在编译期即可得到常量结果,无须分配空间。
而用 const 限定是运行时的常量,可能需要给它分配空间。

.·.·. 发表于 2018-10-9 18:43:26

最开始那段代码有BUG
for(i=1;i<7;i++)printf("%lf,",SIGMA(i));
目测会炸

wayne 发表于 2018-10-9 23:42:45

gxqcn 发表于 2018-10-9 16:21
这里的 constexpr 的作用是在编译期即可得到常量结果,无须分配空间。
而用 const 限定是运行时的常量,可 ...
我的理解就是constexpr的修饰对象是表达式,是一块代码,[代码段],让编译器在编译期可以确定常量的值,但不能保证,所以如果不能确定的话,就会退化成 运行期的工作了。这样的好处就是不用写两套代码了。
=====
而const的修饰对象是变量,是有内存分配的、是[数据段]

另外,我觉得在精神上,这段代码已经足够简明了。
inline constexpr auto SIGMA( std::size_t n ) noexcept
{
    using D7 = double[ 7 ];

    return D7{ 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 }[ n ];
}
再往下玩,基本上就是语法糖层面的feature了。

gxqcn 发表于 2018-10-10 08:05:59

赞成 wayne 的观点!尤其是对 constexpr 限定作用描述,更全面更准确。

我的目的是从一个常量数组中获取指定下标的数值,而这个数组是临时的,无需分配空间,甚至可以是无名或匿名的。

上面引用的代码,还不得不定义一个新类型“D7”,
我的疑问是:可不可省掉这一步?
即:用 {} 初始化的数组,让编译器自动识别出类型及大小,可以吗?

wayne 发表于 2018-10-10 21:05:09

如果大小不确定,可以不指定大小
constexpr double SIGMA( size_t n ) noexcept
{
    return (double[]){ 0.0, 0.3085375, 0.6914625, 5,0.9331928, 0.9937903, 0.9997674, 0.9999966 };
}

gxqcn 发表于 2018-10-11 07:55:32

上面的写法之前我曾试过,但很遗憾,未编译通过,我用的是 VS2017

wayne 发表于 2018-10-11 11:40:10

确实,查了下。visual studio的C和C++编译器都不支持 compound literal 方式的构造.
https://stackoverflow.com/questions/3869963/compound-literals-in-msvc

------------------------
但是gcc是可以的,测试了下,Linux的和windows的MinGW都支持
页: [1] 2
查看完整版本: 如何优雅地获取常量数列中某个值?