编译器模式,其向量在编译时求值(带有"consteval")



我正试图创建一个遵循构建器模式的类,并且在编译时完全运行(在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++中使用常量时间生成器模式?这似乎只发生在vectors中,我使用的版本支持C++20constexpr vectors。

您的代码不编译,因为当前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。或者其他不使用堆的容器。

相关内容

  • 没有找到相关文章

最新更新