假设我有一个类型:
struct Foo {
int a, b, c, d, e;
};
Run Code Online (Sandbox Code Playgroud)
现在,我想要一个宏(或任何其他解决方案),它可以Foo以某种方式定义一个对象,如果该对象可以constexpr(因为它是用编译时初始化int的),那么它将其定义为static constexpr Foo.如果不能constexpr,那么它定义为const Foo(我在函数范围内使用此宏).
所以,我想要一个宏(或一些等效的解决方案):
#define DEF(a, b, c, d, e) ... // magic here
Run Code Online (Sandbox Code Playgroud)
如果我用编译时常量调用它:
DEF(1, 2, 3, 4, 5);
Run Code Online (Sandbox Code Playgroud)
然后我想将其扩展为:
static constexpr Foo foo{1, 2, 3, 4, 5};
Run Code Online (Sandbox Code Playgroud)
但是,如果任何参数不是编译时常量(所以它不能constexpr):
int b = 2;
DEF(1, b, 3, 4, 5); // second parameter is not a compile-time constant
Run Code Online (Sandbox Code Playgroud)
然后我想:
const Foo foo{1, b, 3, 4, 5};
Run Code Online (Sandbox Code Playgroud)
我希望有这样的东西的原因是编译器不允许远离foo堆栈优化,所以我必须手动进行这种优化.
(注意,我使用Foo了很多地方,这就是为什么我想要一个自动解决方案.目前我需要决定是否foo应该是static个案,这是乏味的.)
我知道你在评论中提到的__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);
}
Run Code Online (Sandbox Code Playgroud)
这会在两种情况下生成正确的代码.看看godbolt
如果有人能想出一些狡猾的SFINAE sheananigans来替换__builtin_constant_p这种环境中的便携式东西,那么你就会开展业务.
说明:此处的真正关键是临时生命周期扩展.有一个原因是有一个宏吐出静态关键字将是一个巨大的麻烦,所以让我们不要打扰它!
A const Foo&完全能够指向a static const,并且只要将常规const构建为临时的,生命周期扩展将(针对每个意图和目的)在编译期间将引用提升为常规变量.另外,请记住引用没有自己的地址,因此链接问题中解释的问题不适用于它们.
有了decltype(auto),我们就可以创建一个可以返回一个临时的功能Foo或const Foo&将填充该const参考.
最后,将它打包在lambda中(与辅助函数/模板相对)允许我们轻松区分文字与命名变量,并允许编译器确定静态变量使用常量表达式进行初始化.这一点非常重要,因为一系列线程安全样板将会在最轻微的模糊处理中得到解决.