具有在编译时评估的向量的构建器模式(使用“consteval”)

meg*_*ayu 3 c++ design-patterns builder c++20

我试图创建一个遵循构建器模式并且在编译时完全运行的类(使用constevalC++20 中的 new 关键字),但无论我尝试什么都不起作用。例如,这是行不通的:

#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();
}
Run Code Online (Sandbox Code Playgroud)

这给出了错误“<anonymous>不是常量表达式”。这让我相信我应该返回该类的副本:

#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();
}
Run Code Online (Sandbox Code Playgroud)

然而,我遇到了同样的错误。我应该如何在 C++ 中使用常量时间构建器模式?看来这只发生在vectors 上,而我使用的是支持 C++20 s 的版本constexpr vector

Bri*_*ian 6

您的代码无法编译,因为当前的 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];
}
Run Code Online (Sandbox Code Playgroud)

请注意,临时MyClass对象(以及std::vector<int>子对象)将被销毁,因此所有动态分配都将在返回之前被清理foo()

您想在最外面的函数完成后保留向量consteval吗?抱歉,您不能这样做——至少在当前版本的 C++ 中不能。您需要将其内容复制到std::array不使用动态分配的对象或其他类型的对象中。