假设我有一个类型:
struct Foo {
int a, b, c, d, e;
};
现在,我想拥有一个宏(或任何其他解决方案),该宏可以以某种方式定义Foo
对象,如果该对象可以是constexpr
(因为它已初始化) (int
s),然后将其定义为static constexpr Foo
。如果不能为constexpr
,则它将定义为const Foo
(我将在函数作用域中使用此宏)。
所以,我想要一个宏(或一些等效的解决方案):
#define DEF(a, b, c, d, e) ... // magic here
如果我使用编译时常量来调用它:
DEF(1, 2, 3, 4, 5);
然后我想将其扩展为:
static constexpr Foo foo{1, 2, 3, 4, 5};
但是,如果任何参数不是编译时常量(因此不能为constexpr
):
int b = 2;
DEF(1, b, 3, 4, 5); // second parameter is not a compile-time constant
那我想拥有:
const Foo foo{1, b, 3, 4, 5};
我想要这样的原因是编译器not allowed可以从堆栈中优化foo
,所以我必须手动进行此优化。
(请注意,我在很多地方都使用Foo
,这就是为什么我想拥有一个自动解决方案的原因。目前,我需要确定foo
应该是static
还是不是逐个案例,这很乏味。)
答案 0 :(得分:5)
我知道您在评论中提到__builtin_constant_p
不适合您,因为您想要一个可移植的解决方案,但是如果其他人偶然发现了这个问题,则可以肯定地使用它来解决此问题:
通过结合decltype(auto)
,自动lambda捕获和临时生命周期延长,我们可以执行以下操作:
struct Foo {
int a;
int b;
int c;
};
void worker(const Foo*);
#define DEF(A, B, C) \
[&]() -> decltype(auto) { \
constexpr bool test = \
__builtin_constant_p(A) && \
__builtin_constant_p(B) && \
__builtin_constant_p(C); \
\
if constexpr(test) { \
static const Foo res {A, B, C}; \
return (res); \
} \
else { \
return Foo{A, B, C}; \
} \
}() \
//end of macro
void foo(int v) {
const Foo& x = DEF(1, 2, 3);
//const Foo& x = DEF(1, v, 3);
worker(&x);
}
在两种情况下都会生成正确的代码。请参见godbolt
如果有人可以提出一些狡猾的SFINAE sheananigans来代替__builtin_constant_p
,而在这种情况下,这种东西可以随身携带,那么您就可以做生意。
说明:此处的真正关键是临时的生存期延长。原因是让宏吐出static关键字将是一个巨大的麻烦,所以让我们不必理会它!
const Foo&
完全能够指向static const
,并且只要将常规const
作为临时对象构建,生命周期扩展将(针对每种意图和目的)促进编译期间将引用转换为常规变量。另外,请记住,引用没有自己的地址,因此链接问题中解释的问题不适用于它们。
然后使用decltype(auto)
,我们可以创建一个函数,该函数可以返回临时Foo
或const Foo&
,该函数将填充该const
引用。
最后,将其包装在lambda中(与辅助函数/模板相对),使我们可以轻松地区分文字和命名变量,并允许编译器确定使用常量表达式初始化静态变量。这很重要,因为一堆线程安全样板将在丝毫模棱两可的情况下被粘贴。