数学研发论坛

 找回密码
 欢迎注册
查看: 151|回复: 17

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

[复制链接]
发表于 2018-10-9 10:12:18 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?欢迎注册

x
问题是这样:

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

  7. #define SIGMA( n )                  ( _##n##_SIGMA_ )
复制代码


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

  4.     return sigma[ n ];
  5. }
复制代码


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

  4.     return D7{ 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 }[ n ];
  5. }
复制代码


以上皆能编译通过。



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



如果是你,用 C++ 会如何实现它?并尽可能保持优雅。。。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2018-10-9 10:31:26 | 显示全部楼层
刚刚同事给出一个方案,还算比较“优雅”:
  1. inline constexpr auto SIGMA( std::size_t n ) noexcept
  2. {
  3.     return std::begin( { 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 } )[ n ];
  4. }
复制代码
能够编译通过。

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

如果项目中尚未 include 任何类型的标准库容器,请在文件前加上“#include <initializer_list>”
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2018-10-9 13:55:26 | 显示全部楼层
我或许会用const float SIGMA[]={...};,或许不优雅,哈哈。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2018-10-9 16:21:00 | 显示全部楼层
这里的 constexpr 的作用是在编译期即可得到常量结果,无须分配空间。
而用 const 限定是运行时的常量,可能需要给它分配空间。

点评

是呀,而且对const的计算,是发生在运行时,而非编译时。  发表于 7 天前
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2018-10-9 18:43:26 | 显示全部楼层
最开始那段代码有BUG
for(i=1;i<7;i++)printf("%lf,",SIGMA(i));
目测会炸

点评

你指的是用 C 风格宏定义的那个吧?它必须写数字调用,比如 SIGMA( 6 );而后的 C++ 风格因为是函数调用,没有此限制。  发表于 7 天前
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 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了。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 7 天前 | 显示全部楼层
赞成 wayne 的观点!尤其是对 constexpr 限定作用描述,更全面更准确。

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

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

点评

^_^  发表于 7 天前
return (double[7]){ 0.0, 0.3085375, 0.6914625, 0.9331928, 0.9937903, 0.9997674, 0.9999966 }[n];  发表于 7 天前
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 7 天前 | 显示全部楼层
如果大小不确定,可以不指定大小
  1. constexpr double SIGMA( size_t n ) noexcept
  2. {
  3.     return (double[]){ 0.0, 0.3085375, 0.6914625, 5,0.9331928, 0.9937903, 0.9997674, 0.9999966 }[n];
  4. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 6 天前 | 显示全部楼层
上面的写法之前我曾试过,但很遗憾,未编译通过,我用的是 VS2017
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 6 天前 | 显示全部楼层
确实,查了下。visual studio的C和C++编译器都不支持 compound literal 方式的构造.
https://stackoverflow.com/questi ... nd-literals-in-msvc

------------------------
但是gcc是可以的,测试了下,Linux的和windows的MinGW都支持
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2018-10-17 14:33 , Processed in 0.102870 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表