我正试图创建一个遵循构建器模式的类,并且在编译时完全运行(在C++20中使用新的consteval
关键字),但无论我尝试什么都不起作用。例如,这不起作用:
#include <vector>
class MyClass
{
private:
std::vector<int> data;
public:
consteval MyClass& setData()
{
this->data = {20};
return *this;
}
consteval std::vector<int> build()
{
return data;
}
};
int main()
{
std::vector<int> data = MyClass().setData().build();
}
这给出了错误";CCD_ 2不是常数表达式";。这让我相信我应该返回该类的副本:
#include <vector>
class MyClass
{
private:
std::vector<int> data;
public:
consteval MyClass setData()
{
// https://herbsutter.com/2013/04/05/complex-initialization-for-a-const-variable/
return [&]{
MyClass newClass;
newClass.data = {20};
return newClass;
}();
}
consteval std::vector<int> build()
{
return data;
}
};
int main()
{
std::vector<int> data = MyClass().setData().build();
}
然而,我也犯了同样的错误。我应该如何在C++中使用常量时间生成器模式?这似乎只发生在vector
s中,我使用的版本支持C++20constexpr vector
s。
您的代码不编译,因为当前C++只允许"瞬态";常量表达式中的分配。这意味着,在常数表达式求值期间,允许动态地分配内存(由于C++20),但仅在任何这样的分配在常数表达式为"0"时被解除分配的条件下;结束";。
在代码中,表达式MyClass().setData()
必须是常量表达式,因为它是立即调用(这意味着对consteval
函数的调用,而不是发生在另一个consteval
函数或if consteval
块中的调用)。表达式MyClass().setData().build()
也必须是常量表达式。这意味着,在评估MyClass().setData().build()
时,允许动态分配,但必须不存在"0";幸存的";在CCD_ 11结束时以及在MyClass().setData().build()
结束时的分配。
由于无法阻止MyClass().setData()
的结果具有实时分配,因此只能在封闭的consteval
函数或if consteval
块内调用它。例如,以下内容将有效:
consteval int foo() {
return MyClass().setData().build()[0];
}
请注意,临时MyClass
对象(以及std::vector<int>
子对象)将被销毁,因此,在foo()
返回之前,所有动态分配都将被清理。
您想在最外层的consteval
函数完成后保持矢量不变吗?很抱歉,你不能这么做——至少在当前版本的C++中不能。您需要将其内容复制到<anonymous>
0或其他不使用动态分配的对象中。
<source>: In function 'int main()':
<source>:24:46: error: '<anonymous>' is not a constant expression
24 | std::vector<int> data = MyClass().setData().build();
| ~~~~~~~~~~~~~~~~~^~
请注意编译器是如何解开MyClass().setData()
的。
setData()
返回对*this
的引用,其类型为MyClass
而不是const
。实际上,您只是在setData()
中修改了它。对非常量的引用不能是常量表达式。
您可以通过按值返回来更改:consteval MyClass setData()
但随后您会遇到这样的问题,即在constexpr中,所有对new的调用都必须与对delete和elided的调用相平衡。但是std::vector
调用new
,并且您永远不会破坏向量。所以你得到了:
/opt/compiler explorer/gcc-12.1.0/include/c++/12.1.0/bits/allocater.h:182:50:错误:"MyClass::setData()"不是常量表达式,因为它引用了"operator new"的结果
如果使用std::array
,则可以执行consteval
。或者其他不使用堆的容器。