在C++中,缩短`constexpr`对象的生命周期是否合法?

7

对于普通对象(即使是const对象),通过显式调用析构函数来结束它们的生命周期是允许的。之后,程序可以使用放置new在相同的内存位置开始另一个对象生命周期。

但是调用constexpr对象的析构函数是否合法?它是否会导致一些有用或至少良好构造的程序?

很容易想象相反的情况:

struct A{
    int v = 0;
    constexpr void foo() { v ? throw 1 : ++v; }
    constexpr ~A() { foo(); }
};

constexpr A y;

int main() { y.~A(); y.~A(); }

这个(很可能格式不正确的)程序在任何编译器中被接受而没有任何警告:https://gcc.godbolt.org/z/aqMbfjxKT。在Clang中,它通过A类的constexpr析构函数抛出了异常。

据我所了解,constexpr用于在编译时评估事物。但是,如果您在运行时创建实例,则它们的行为类似于普通代码。在这种情况下,我认为您真正的问题是调用了两次析构函数。至少对于我来说,如果您删除第二个析构函数,编译器探索器不会为clang抛出异常。 - Pepijn Kramer
有趣的问题。但是,如果程序格式不正确(我不确定它是否如此),那么讨论编译后代码在运行时的行为可能有点无关紧要。 - dreamlax
2
在constexpr上下文中评估代码可以检测到UB;对于clang而言:在常量表达式中不允许销毁超出其生命周期的对象。演示 - Jarod42
1
你刚刚进入了你的代码中的 https://timsong-cpp.github.io/cppwp/n4868/class.dtor#19.sentence-1,`constexpr` 在这里是无关紧要的。你可能想知道在常量表达式上下文中调用析构函数会发生什么? - Language Lawyer
1
该程序可以使用放置new在相同的内存位置启动另一个对象生命周期。但是,new(&y) A;不会有效,因为&yconst A*,而没有将新的A放置在内存中后,在对象生命周期结束后会调用其析构函数。您需要强制转换掉const才能使用放置new,但这样会产生未定义行为。 - Ted Lyngmo
1个回答

1

[dcl.constexpr] 在对象声明中使用 constexpr 说明符将对象声明为 const。

您可以像处理其他任何 const 对象一样处理此类对象。除了暗示声明的对象是 const 外,constexpr 似乎不会影响对象本身的任何内容。它仅影响程序的静态属性,即哪些表达式是常量表达式,哪些模板实例化是有效的等等。没有针对使用 constexpr 说明符声明的对象的运行时行为的特殊规定。

标准在几个地方使用类似“constexpr 对象”的短语,但这似乎是一个误称。在我看来,它应该指的是 constexpr 变量而不是“constexpr 对象”。不应该有“constexpr 对象”(就像没有内联对象一样),只有 constexpr 变量(就像有内联变量一样)。毕竟,constexpr 是一个 decl-specifier,就像 inline 一样。这些变量所引用的对象只是 const。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接