本文共 4846 字,大约阅读时间需要 16 分钟。
这个系列是从开始的,主要是复现Jason Turner的“C++ Weekly With Jason Turner”视频中的代码。
Jason 在这期和后面几期讨论了constexpr的一些用法,非常有意思。把部分简单的运算从运行时移动到了编译时,可以提高运行效率。但是我还没有真正找到必须要这样做的的实际样例,主要是我写的代码,瓶颈都不在这种地方(瓶颈多了去了, 呵呵)。
C++17 中constexpr
可以使用std::array
和lambda函数了。这里搬运Jason的代码,并做了一丁点简单调整,如下所示。代码中生成了一个std::array
,里面保存有16种颜色配置,需要生成一个新的array,内保存了上述16种颜色按照luma(可理解为亮度)从小到大排序的结果。所有运算均在编译时完成。
这是一个很好的例子解释了我们在编写c++代码时,我们是在设法和compiler沟通。compiler能做什么,做了什么,我们基本说的不算的,呵呵。
#include#include #include #include struct Color { std::uint8_t num; std::uint8_t r; std::uint8_t g; std::uint8_t b; double luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; };template < typename Colors >constexpr auto nearest_color( const Colors& colors, const std::uint8_t r, const std::uint8_t g, const std::uint8_t b ) { return *std::min_element( begin(colors), end(colors), [r, g, b](const auto& lhs, const auto& rhs){ const auto square = [](const auto& t){ return t*t; }; return ( square( lhs.r - r ) + square( lhs.g - g ) + square( lhs.b - b ) ) < ( square( rhs.r - r ) + square( rhs.g - g ) + square( rhs.b - b ) ); } ); }template < std::size_t N >constexpr auto sort_by_luma( const std::array< Color, N >& colors ) { auto retVal = colors; const auto arrayHead = &std::get<0>(retVal); const auto end = arrayHead + colors.size(); for ( std::size_t i = 0; i < colors.size(); ++i ) { const auto begin = arrayHead + i; auto minElem = std::min_element( begin, end, [](const auto& lhs, const auto& rhs){ return lhs.luma < rhs.luma; } ); const auto tmp = *minElem; *minElem = *begin; *begin = tmp; } return retVal;}int main() { constexpr std::array< Color, 16 > colors { { Color{ 0, 0x00, 0x00, 0x00}, Color{ 1, 0xFF, 0xFF, 0xFF}, Color{ 2, 0x88, 0x39, 0x32}, Color{ 3, 0x67, 0xB6, 0xBD}, Color{ 4, 0x8B, 0x3F, 0x96}, Color{ 5, 0x55, 0xA0, 0x49}, Color{ 6, 0x40, 0x31, 0x8D}, Color{ 7, 0xBF, 0xCE, 0x72}, Color{ 8, 0x8B, 0x54, 0x29}, Color{ 9, 0x57, 0x42, 0x00}, Color{ 10, 0xB8, 0x69, 0x62}, Color{ 11, 0x50, 0x50, 0x50}, Color{ 12, 0x78, 0x78, 0x78}, Color{ 13, 0x94, 0xE0, 0x89}, Color{ 14, 0x78, 0x69, 0xC4}, Color{ 15, 0x9F, 0x9F, 0x9F} }}; static_assert( 12 == nearest_color( colors, 128, 128, 128 ).num ); static_assert( 0 == nearest_color( colors, 0, 0, 0 ).num ); constexpr auto sorted_colors = sort_by_luma( colors ); static_assert(sorted_colors[ 0].num == 0); static_assert(sorted_colors[ 7].num == 14); static_assert(sorted_colors[ 8].num == 12); static_assert(sorted_colors[15].num == 1); for( const auto& c : sorted_colors ) { std::cout << static_cast (c.num) << ": " << c.luma << '\n'; } return sorted_colors[15].num;}
若运行这段代码,终端输出的是sorted_colors
的内容,
0: 06: 58.83149: 65.69942: 73.2911: 804: 85.4398: 92.588414: 114.75912: 12010: 121.295: 137.77415: 1593: 165.717: 196.16913: 201.5611: 255
在complier explorer上,去掉所有和std::cout
相关的代码,编译优化为-O3
时,得到的汇编非常简单,就直接是一条mov
加一条ret
,就完事了,真神奇!见。
我们甚至可以使用constexpr
在编译时生成随机数!先不要管这个是用来作什么的,但是听上去很酷。
#include#include constexpr auto seed() { std::uint64_t shifted = 0; for ( const auto c : __TIME__ ) { shifted <<= 8; shifted |= c; } return shifted;}struct PCG { struct pcg_32_random_t { std::uint64_t state = 0; std::uint64_t inc = seed(); }; pcg_32_random_t rng; typedef std::uint32_t ResultType_T; constexpr ResultType_T operator()() { return pcg_32_random_r(); } static ResultType_T constexpr min() { return std::numeric_limits ::min(); } static ResultType_T constexpr max() { return std::numeric_limits ::max(); }private: constexpr ResultType_T pcg_32_random_r() { std::uint64_t oldState = rng.state; rng.state = oldState * 6364136223846793005ULL + ( rng.inc|1 ); std::uint32_t xorshifted = ((oldState >> 18u) ^ oldState) >> 27u; std::uint32_t rot = oldState >> 59u; return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); }};constexpr auto get_random(int x) { PCG pcg; while ( x > 0 ) { pcg(); --x; } return pcg();}int main() { constexpr auto r = get_random(10); return r;}
上述代码每次编译,main()
的返回值都是一个随机的常量(基本随机,seed是通过时间生成的)。但是我在complier explorer上测试时,感觉并seed不一定是随时间变化的。但是在本地通过objdump -d
命令输出汇编显示,每次编译出的随机数都是不同的。神奇!
转载地址:http://jiugf.baihongyu.com/