【发布时间】:2019-09-03 05:42:16
【问题描述】:
当我尝试编译这段代码时:
#include <iostream>
#include <limits.h>
// End recursive template-expansion of function select below.
template <typename Type>
static inline constexpr Type select(unsigned index)
{ return Type(); }
// Select one of the items passed to it.
// e.g. select(0, a, b, c) = a; select(1, a, b, c) = b; etc.
template <typename Type, typename... Params>
[[gnu::always_inline]]
static inline constexpr Type select(unsigned index, Type value, Params... values)
{ return index == 0 ? value : select<Type>(index - 1, values...); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_1(Type mask, Type shift, Type value)
{ return ((value & mask) >> shift) | ((value << shift) & mask); }
template <typename Type>
[[gnu::always_inline]]
static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
{
return i == 0
? value
: reflect_mask_helper_0(
i - 1,
reflect_mask_helper_1<Type>(
select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
1 << (i - 1),
value));
}
template <typename Type>
[[gnu::flatten]]
static inline constexpr Type reflect_mask(Type value)
{ return reflect_mask_helper_0(__builtin_ctz(sizeof(Type) * CHAR_BIT), value); }
int main(void) {
for (int i = 0; i < 65536; i++) {
std::cout << reflect_mask<uint16_t>(i) << std::endl;
}
}
gcc 给我一个错误,说函数 reflect_mask_helper_0 不能内联,因为它是递归的。但是,函数select 也是递归的,但 gcc 将其内联而不抱怨。我在这里错过了什么?
(我需要它是递归的,因为constexpr 函数在 C++11 下不能包含循环。)
错误信息:
% g++ test.cpp -O3 -march=native -c
test.cpp: In function ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’:
test.cpp:23:30: error: inlining failed in call to always_inline ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’: recursive inlining
23 | static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
| ^~~~~~~~~~~~~~~~~~~~~
test.cpp:27:28: note: called from here
27 | : reflect_mask_helper_0(
| ~~~~~~~~~~~~~~~~~~~~~^
28 | i - 1,
| ~~~~~~
29 | reflect_mask_helper_1<Type>(
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 | 0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 | 1 << (i - 1),
| ~~~~~~~~~~~~~
33 | value));
| ~~~~~~~
test.cpp: In function ‘int main()’:
test.cpp:23:30: error: inlining failed in call to always_inline ‘constexpr Type reflect_mask_helper_0(unsigned int, Type) [with Type = short unsigned int]’: recursive inlining
23 | static inline constexpr Type reflect_mask_helper_0(unsigned i, Type value)
| ^~~~~~~~~~~~~~~~~~~~~
test.cpp:27:28: note: called from here
27 | : reflect_mask_helper_0(
| ~~~~~~~~~~~~~~~~~~~~~^
28 | i - 1,
| ~~~~~~
29 | reflect_mask_helper_1<Type>(
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | select(i - 1, 0xaaaaaaaaaaaaaaaa, 0xcccccccccccccccc, 0xf0f0f0f0f0f0f0f0,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 | 0xff00ff00ff00ff00, 0xffff0000ffff0000, 0xffffffff00000000),
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 | 1 << (i - 1),
| ~~~~~~~~~~~~~
33 | value));
| ~~~~~~~
【问题讨论】:
-
如果您将
i转换为泛型参数并为0提供非递归特化,则应该可以内联 -
不确定您使用的是哪个版本,我在
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4上尝试过,编译后没有任何错误/警告。 -
@sklott,我在 Arch Linux 中使用 gcc 9.1.0。
-
您知道,答案部分属于解决方案。把它放在问题中只是令人困惑。我建议您将其发布为答案并自我接受。没有难过的感觉。
标签: c++ recursion compiler-optimization micro-optimization inlining