在编译时评估向量的构建器模式(使用 `consteval`)
Builder patterns with vectors evaluated at compile time (with `consteval`)
我正在尝试创建一个遵循构建器模式并在编译时完全运行的 class(使用 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();
}
给出错误“<anonymous>
不是常量表达式”。这让我相信我应该改为 return 份 class 的副本:
#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++20 constexpr vector
s 的版本。
您的代码无法编译,因为当前的 C++ 只允许在常量表达式中进行“瞬态”分配。这意味着在常量表达式计算期间,允许动态分配内存 (C++20 起),但前提是任何此类分配在常量表达式“结束”时被释放。
在您的代码中,表达式 MyClass().setData()
必须是常量表达式,因为它是一个 立即调用 (这意味着调用 consteval
函数,除了出现在另一个 consteval
函数内或 if consteval
块内的)。表达式 MyClass().setData().build()
也必须是常量表达式。这意味着,在评估 MyClass().setData().build()
时,允许动态分配,但在 MyClass().setData()
末尾和 MyClass().setData().build()
末尾都不能有“幸存”分配。
由于无法阻止 MyClass().setData()
的结果进行实时分配,因此您只能在封闭的 consteval
函数或 if consteval
块中调用它。例如,以下将是有效的:
consteval int foo() {
return MyClass().setData().build()[0];
}
请注意,临时 MyClass
对象(以及 std::vector<int>
子对象)将被销毁,因此所有动态分配都将被清除,就在 foo()
[=39 之前=].
您想在最外层的 consteval
函数完成后保留向量吗?抱歉,您不能那样做——至少在当前版本的 C++ 中不能。您需要将其内容复制到 std::array
或其他不使用动态分配的对象中。
<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()
returns 对 *this
的引用,其类型为 MyClass
而不是 const
。您实际上只是在 setData()
中修改了它。对 non-const 的引用不能是常量表达式。
您可以通过按值返回来更改它:consteval MyClass setData()
但是你 运行 遇到了一个问题,即在 constexpr 中,所有对 new 的调用必须与对 delete 和 elided 的调用保持平衡。但是 std::vector
调用 new
并且您永远不会破坏向量。所以你得到:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/allocator.h:182:50: error: 'MyClass::setData()()' is not a constant expression because it refers to a result of 'operator new'
如果你使用std::array
,你可以做到consteval
。或者其他一些不使用堆的容器。
我正在尝试创建一个遵循构建器模式并在编译时完全运行的 class(使用 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();
}
给出错误“<anonymous>
不是常量表达式”。这让我相信我应该改为 return 份 class 的副本:
#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++20 constexpr vector
s 的版本。
您的代码无法编译,因为当前的 C++ 只允许在常量表达式中进行“瞬态”分配。这意味着在常量表达式计算期间,允许动态分配内存 (C++20 起),但前提是任何此类分配在常量表达式“结束”时被释放。
在您的代码中,表达式 MyClass().setData()
必须是常量表达式,因为它是一个 立即调用 (这意味着调用 consteval
函数,除了出现在另一个 consteval
函数内或 if consteval
块内的)。表达式 MyClass().setData().build()
也必须是常量表达式。这意味着,在评估 MyClass().setData().build()
时,允许动态分配,但在 MyClass().setData()
末尾和 MyClass().setData().build()
末尾都不能有“幸存”分配。
由于无法阻止 MyClass().setData()
的结果进行实时分配,因此您只能在封闭的 consteval
函数或 if consteval
块中调用它。例如,以下将是有效的:
consteval int foo() {
return MyClass().setData().build()[0];
}
请注意,临时 MyClass
对象(以及 std::vector<int>
子对象)将被销毁,因此所有动态分配都将被清除,就在 foo()
[=39 之前=].
您想在最外层的 consteval
函数完成后保留向量吗?抱歉,您不能那样做——至少在当前版本的 C++ 中不能。您需要将其内容复制到 std::array
或其他不使用动态分配的对象中。
<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()
returns 对 *this
的引用,其类型为 MyClass
而不是 const
。您实际上只是在 setData()
中修改了它。对 non-const 的引用不能是常量表达式。
您可以通过按值返回来更改它:consteval MyClass setData()
但是你 运行 遇到了一个问题,即在 constexpr 中,所有对 new 的调用必须与对 delete 和 elided 的调用保持平衡。但是 std::vector
调用 new
并且您永远不会破坏向量。所以你得到:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/allocator.h:182:50: error: 'MyClass::setData()()' is not a constant expression because it refers to a result of 'operator new'
如果你使用std::array
,你可以做到consteval
。或者其他一些不使用堆的容器。