Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;20:std::array作为非类型模板参数重新排列元素_C++_Templates_G++_C++20 - Fatal编程技术网

C++ C++;20:std::array作为非类型模板参数重新排列元素

C++ C++;20:std::array作为非类型模板参数重新排列元素,c++,templates,g++,c++20,C++,Templates,G++,C++20,我最近实现了一个Builder类,但我希望避免抛出异常。所以我有一个想法,我可以用一个布尔数组来参数化构建器,该数组表示已设置的字段。每个setter将返回一个新的构建器专业化,并设置相应的字段标志。这样我就可以检查在编译时是否设置了正确的字段 发现复杂数据类型作为非类型模板参数仅在C++ 20中可用。但我还是用它做了实验 结果发现它的行为异常。当返回每个新的专业化时,“true”标志会在开始时聚集起来,如此示例调试输出所示: - set field 4 old flags 00000 new

我最近实现了一个Builder类,但我希望避免抛出异常。所以我有一个想法,我可以用一个布尔数组来参数化构建器,该数组表示已设置的字段。每个setter将返回一个新的构建器专业化,并设置相应的字段标志。这样我就可以检查在编译时是否设置了正确的字段

发现复杂数据类型作为非类型模板参数仅在C++ 20中可用。但我还是用它做了实验

结果发现它的行为异常。当返回每个新的专业化时,“true”标志会在开始时聚集起来,如此示例调试输出所示:

 - set field 4 old flags 00000 new flags 00001
 - set field 2 old flags 10000 new flags 10100
 - set field 0 old flags 11000 new flags 11000
 - set field 3 old flags 11000 new flags 11010
 - set field 1 old flags 11100 new flags 11100
这些是下面两行中的第二行。删除第一个实例修复了问题,这表明第一个实例在某种程度上影响了第二个实例

Fields fields1 = Builder().SetFirst(1).SetSecond(2).SetThird(3).SetFourth(4).SetFifth(5).Build();
Fields fields2 = Builder().SetFifth(5).SetThird(3).SetFirst(1).SetFourth(4).SetSecond(2).Build();
它应该这样做吗?这只是我在某种程度上丢失的C++ 20的一个微妙之处,还是GCC中的一个bug?< /P> 我用gcc 9.3.0和gcc 10.2.0对此进行了检查。我还尝试从git编译,版本11.0.1更改为18EBD6C439。命令行是
g++-Wall--std=c++2a builder.cpp
。他们的行为都一样。我还搜索了gcc的bugzilla,但找不到任何类似的东西

下面是两个代码示例。首先,我尽可能地将一个版本剥离回去,以显示问题所在。第二个例子展示了我试图实现的目标的更多背景。(还有第三个更现实的版本,但公开发布可能会有问题。)

#包括
#包括
使用Flags=std::array;
模板
类生成器
{
公众:
建筑商(){
}
自动设置优先(){
constexpr auto new_flags=SetFieldFlag();
建筑商新建筑商;
返回新的构建器;
}
自动设置秒(){
constexpr auto new_flags=SetFieldFlag();
建筑商新建筑商;
返回新的构建器;
}
Flags GetFlags()常量{
返回旗;
}
私人:
模板
静态constexpr auto SetFieldFlag(){
自动新建标志=标志;
std::get(新的_标志)=true;
返回新的_标志;
}
};
int main()
{
自动标记1=Builder().SetFirst().SetSecond().GetFlags();
断言(flags1[0]);
断言(标记1[1]);
auto flags2=Builder().SetSecond().SetFirst().GetFlags();
断言(flags2[0]);
断言(flags2[1]);
返回0;
}
#包括
#包括
constexpr int NumFields=5;
使用Flags=std::array;
使用Fields=std::array;
std::ostream&operator我曾经检查过为多个编译器生成的代码,这确实是gcc中的一个bug。clang和msvc都能产生正确的结果

下面是有趣的部分,为方法
Builder::SetSecond()
生成的汇编程序,它在您的简短示例中导致了错误。实际代码并不重要,通过查看以下类型可以看到错误:

Builder::SetSecond():#@Builder::SetSecond()
推动rbp
mov rbp,rsp
副区长,32
mov qword ptr[rbp-8],rdi
mov ax,word ptr[.L_uuuconst.Builder::SetSecond().新的\u标志]
mov字ptr[rbp-16],ax
李尔迪[rbp-24]
调用生成器::生成器()[基本对象构造函数]
加上rsp,32
流行限制性商业惯例
ret

Builder::SetSecond():
推动rbp
mov rbp,rsp
推送rbx
副区长,40
mov QWORD PTR[rbp-40],rdi
mov字PTR[rbp-18],0
mov字节PTR[rbp-17],1
lea rax,[rbp-19]
莫夫尔迪,拉克斯
调用生成器::生成器()[完成对象构造函数]
不
mov-eax,ebx
mov rbx,QWORD PTR[rbp-8]
离开
ret
如果您比较得到
调用的函数类型,您可以清楚地看到,在gcc中,
SetSecond()
没有设置第二个——有
{true}
,但应该是
{false,true}


那么,是时候换成叮当声了?

Ha!事实上,我通常在CLAN中工作,但是当我知道直到C++ 20之前,这个特性才被支持时,我不知怎么地感觉到G++是最简单的C++ 20的实验方法。事实上,这一切都不现实:现实生活中,我还是被C++ 14所困扰。但至少有了这些证据,我可以向gcc的bugzilla提交一份bug报告。谢谢!此处向gcc报告的错误:
#include <array>
#include <cassert>

using Flags = std::array<bool, 2>;

template<Flags flags = Flags{}>
class Builder
{
public:
    Builder() {
    }

    auto SetFirst() {
        constexpr auto new_flags = SetFieldFlag<0>();
        Builder<new_flags> new_builder;
        return new_builder;
    }

    auto SetSecond() {
        constexpr auto new_flags = SetFieldFlag<1>();
        Builder<new_flags> new_builder;
        return new_builder;
    }

    Flags GetFlags() const {
        return flags;
    }

private:
    template<int field>
    static constexpr auto SetFieldFlag() {
        auto new_flags = flags;
        std::get<field>(new_flags) = true;
        return new_flags;
    }
};

int main()
{
    auto flags1 = Builder().SetFirst().SetSecond().GetFlags();
    assert(flags1[0]);
    assert(flags1[1]);

    auto flags2 = Builder().SetSecond().SetFirst().GetFlags();
    assert(flags2[0]);
    assert(flags2[1]);

    return 0;
}
#include <iostream>
#include <array>

constexpr int NumFields = 5;
using Flags = std::array<bool, NumFields>;
using Fields = std::array<int, NumFields>;

std::ostream& operator<<(std::ostream& out, Flags flags) {
    for (int i = 0; i < NumFields; ++i) {
        out << flags[i];
    }
    return out;    
}

std::ostream& operator<<(std::ostream& out, Fields fields) {
    for (int i = 0; i < NumFields; ++i) {
        out << (i ? ":" : "") << fields[i];
    }
    return out;    
}

template<Flags flags = Flags{}>
class Builder
{
public:
    Builder(Fields fields_in = Fields{})
        : fields(fields_in) {
    }

    auto SetFirst(int value) {
        fields.at(0) = value;
        return BuilderWithField<0>();
    }

    auto SetSecond(int value) {
        fields.at(1) = value;
        return BuilderWithField<1>();
    }

    auto SetThird(int value) {
        fields.at(2) = value;
        return BuilderWithField<2>();
    }

    auto SetFourth(int value) {
        fields.at(3) = value;
        return BuilderWithField<3>();
    }

    auto SetFifth(int value) {
        fields.at(4) = value;
        return BuilderWithField<4>();
    }

    Fields Build() {
        std::cout << " - build with flags " << flags << std::endl;
        static_assert(std::get<0>(flags), "first field not set");
        static_assert(std::get<1>(flags), "second field not set");
        static_assert(std::get<2>(flags), "third field not set");
        static_assert(std::get<3>(flags), "fourth field not set");
        static_assert(std::get<4>(flags), "fifth field not set");
        return fields;
    }

private:
    template<int field>
    static constexpr auto SetFieldFlag() {
        auto new_flags = flags;
        std::get<field>(new_flags) = true;
        return new_flags;
    }

    template<int field>
    auto BuilderWithField() {
        constexpr auto new_flags = SetFieldFlag<field>();
        std::cout << " - set field " << field << " old flags " << flags << " new flags " << new_flags << std::endl;
        Builder<new_flags> new_builder(fields);
        return new_builder;
    }

    Fields fields;
};

int main()
{
    Fields fields1 = Builder().SetFirst(1).SetSecond(2).SetThird(3).SetFourth(4).SetFifth(5).Build();
    std::cout << fields1 << std::endl;

    Fields fields2 = Builder().SetFifth(5).SetThird(3).SetFirst(1).SetFourth(4).SetSecond(2).Build();
    std::cout << fields2 << std::endl;

    return 0;
}
Builder<std::array<bool, 2ul>{}>::SetSecond(): # @Builder<std::array<bool, 2ul>{}>::SetSecond()
    push    rbp
    mov     rbp, rsp
    sub     rsp, 32
    mov     qword ptr [rbp - 8], rdi
    mov     ax, word ptr [.L__const.Builder<std::array<bool, 2ul>{}>::SetSecond().new_flags]
    mov     word ptr [rbp - 16], ax
    lea     rdi, [rbp - 24]
    call    Builder<std::array<bool, 2ul>{bool [2]{false, true}}>::Builder() [base object constructor]
    add     rsp, 32
    pop     rbp
    ret
Builder<std::array<bool, 2ul>{}>::SetSecond():
    push    rbp
    mov     rbp, rsp
    push    rbx
    sub     rsp, 40
    mov     QWORD PTR [rbp-40], rdi
    mov     WORD PTR [rbp-18], 0
    mov     BYTE PTR [rbp-17], 1
    lea     rax, [rbp-19]
    mov     rdi, rax
    call    Builder<std::array<bool, 2ul>{bool [2]{true}}>::Builder() [complete object constructor]
    nop
    mov     eax, ebx
    mov     rbx, QWORD PTR [rbp-8]
    leave
    ret