是否可以自动定义变量静态或非静态,具体取决于初始化是否为constexpr?

gez*_*eza 10 c++ c++17

假设我有一个类型:

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个案,这是乏味的.)

Fra*_*ank 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);
}
Run Code Online (Sandbox Code Playgroud)

这会在两种情况下生成正确的代码.看看godbolt

如果有人能想出一些狡猾的SFINAE sheananigans来替换__builtin_constant_p这种环境中的便携式东西,那么你就会开展业务.

说明:此处的真正关键是临时生命周期扩展.有一个原因是有一个宏吐出静态关键字将是一个巨大的麻烦,所以让我们不要打扰它!

A const Foo&完全能够指向a static const,并且只要将常规const构建为临时的,生命周期扩展将(针对每个意图和目的)在编译期间将引用提升为常规变量.另外,请记住引用没有自己的地址,因此链接问题中解释的问题不适用于它们.

有了decltype(auto),我们就可以创建一个可以返回一个临时的功能Fooconst Foo&将填充该const参考.

最后,将它打包在lambda中(与辅助函数/模板相对)允许我们轻松区分文字与命名变量,并允许编译器确定静态变量使用常量表达式进行初始化.这一点非常重要,因为一系列线程安全样板将会在最轻微的模糊处理中得到解决.