当前位置: 首页 > 知识库问答 >
问题:

有没有什么方法可以让康斯特普尔对象引用/指向其他非静态的康斯特普尔对象?

包修贤
2023-03-14

假设我想在编译时使用某种算法构建一个图形,然后计算图形中最终有多少节点。对于 constexpr 而不是模板元编程来说,这似乎是一个理想的情况,因为目标是产生值的计算,而不是真正的类型。我有一些有效的代码,但功能太新了,我担心编译器很宽容,我可以将部分标准解释为说我不能这样做。

#include <iostream>

struct A { int x; constexpr A(int i) noexcept : x{i} {} };
struct B { A& a; constexpr B(A& a) noexcept : a{a} {} };

constexpr int foo() { 
    A a{55};
    B b{a};
    return b.a.x;
}

template<int N>
void output()
{
    std::cout << N << std::endl;
}

int main() {
    // to be absolutely sure compile time eval'd,
    // pass as template arg
    constexpr auto b = foo();
    output<b>();
}

ab实例都是在编译时创建的,它们具有相同的生存期,因此应该是“安全的”。但a是一个非静态对象,标准的这一部分似乎表示这是不允许的:

如果实体是具有静态存储持续时间的对象,不是临时对象,或者是其值满足上述约束的临时对象,或者它是一个函数,则该实体是常量表达式的允许结果。

那么我能还是不能?GCC和clang都很好。

共有3个答案

庄高谊
2023-03-14

在您的示例中:

B b{a};

b不是constexpr变量,草案C14节7.1.5[dcl.constexpr]p5中指出:

对象声明中使用的constexpr说明符将对象声明为const。此类对象应为文字类型,并应进行初始化。如果由构造函数调用初始化,则该调用应为常量表达式(5.19)。否则,或者如果引用声明中使用了constexpr说明符,则其初始值设定项中出现的每个完整表达式都应为常量表达式。[注意:在转换初始值设定项表达式时使用的每个隐式转换和用于初始化的每个构造函数调用都是这种完整表达式的一部分。-end Note]

不适用,但如果我们稍微修改您的示例:

int main() {
    constexpr auto b = foo();
    A a1(42) ;
    constexpr B b1( a1 ) ;
}

并引入b1,这是一个Constexr变量,那么这将不起作用(现场查看),clang说:

error: 'B{a1}' is not a constant expression
     constexpr B b1( a1 ) ;
                        ^

如果我们进一步修改上面的示例,如下所示:

static A a1(42) ;
constexpr B b1( a1 ) ;

它会起作用。constexpr 函数可用于 constexpr,但如果它不满足要求,则不会产生常量表达式。

欧镜
2023-03-14

简而言之,您不能在编译时传递非静态/临时值作为引用。可以将静态/全局值作为constexpr引用传递。但其他任何东西在编译时都不可用。

constexpr void foo() {
    int a; // run-time value.
    ...
}

一个明显的解决方案是按值传递。它发生在编译时,因此您可能无法获得通常的优化,但它也会在编译时发生。

http://ideone.com/J7mVj5

#include <iostream>

struct A { int x; constexpr A(int i) noexcept : x{i} {} };
struct B { A a; constexpr B(A a) noexcept : a{a} {} };

constexpr int foo() { 
    B b{55};
    return b.a.x;
}

template<int N>
void output()
{
    std::cout << N << std::endl;
}

int main() {
    // to be absolutely sure compile time eval'd,
    // pass as template arg
    constexpr auto b = foo();
    output<b>();
}

参见http://ideone.com/tw4jzG

皇甫德庸
2023-03-14

是的,你的例子是符合要求的。

C 14放松的特殊之处在于常量表达式求值内部的中间结果本身不需要是常量表达式。

return 将左值到重值的转换应用于 b.a.x,因为该函数按值返回,而 b.a.x 为:

文字类型的非易失性glvalue,指的是其生命周期从e的计算开始的非易失性对象

(N4527 §5.20/2.7.4)

如果您试图保存对b.a.x的(悬空)引用,这将是一个问题。这不是你引用的“常量表达式的允许结果”。

 类似资料:
  • 我知道在Kotlin中,用于声明常量,用于只读属性。然而,我想知道在以下情况下,哪一个更适合使用。 假设我有一个片段,它需要一个用于和的键。我想知道以下两个选项中哪一个更好: 我更喜欢#选项2,因为它清楚地表明是一个常量,值是在编译时确定的。然而,由于它是在顶层声明的,它需要在编译后的java代码中创建一个类,即(假设文件名是)。在#选项1中,没有生成额外的类,尽管的值将在运行时分配,而不是恒定的

  • 我正在使用Springboot和Java8 我有以下url,它与邮递员配合得很好: "http://localhost:8888/gc/goods/getAll" 现在我尝试编写一个自动测试: 当邮递员给我所有的数据回来,我得到以下错误从我的测试: 组织。springframework。网状物客户ResourceAccessException:获取请求“”时发生I/O错误http://localh

  • 我在Kubernetes是个新手。我想知道在kubernetes环境中最好的生产部署场景是什么。 在过去的学派中,我习惯于将Web服务器(例如Nginx或Apache)放在DMZ层,而将其放在其他层(我们称之为层)。这样,只有web服务器在DMZ上,恶意攻击只能在web服务器VM上进行。 据我所知,K8S部署不再需要这种方法;这是因为K8S自己处理网络、吊舱和流量。所以我在考虑最确定的部署方案。

  • 我有一个DigitalOcean Kubernetes集群。我已经通过Helm安装了NGINX入口控制器 我的入口位于默认命名空间中,我的监视位于监视命名空间中。 以下是我安装的图表版本。 我正在尝试获取一些NGINX指标,以便我可以在Grafana中进行监控。 然而,它们都没有出现在普罗米修斯用户界面中。我试着把普罗米修斯的旗帜添加到我所有的入口中,但仍然一无所获。例如 我使用了默认值。ngin

  • 我有一个 rabbitmq 消息队列,许多其他服务在其上报告所谓的的状态更新。现在,在单独的服务(使用SpringBoot编写)上,我需要侦听这些更新并将其转换为Prometheus可擦除的endpoint。 所以我的计划是将传入的对象转换为并将它们注册到中。这是可行的,但只适用于某些点。我还没有弄清楚哪些是可见的,哪些不是,因为看起来这取决于它们在重新启动服务后进入的顺序。我还想不出任何模式,这

  • 我有这段代码,函数strtol接收一个双指针,并且假设字符串“prueba”是一个指向字符串第一个元素的内存地址的指针(与整个链的内存地址相同),那么就好像是在传递一个指针的dir,不知道我解释了没有,反正不起作用:/在这里给出了警告 长int strtol(常量char*str,char**endptr,int base) strtol根据给定的基将字符串在str中的初始部分转换为一个长的int