https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99460

            Bug ID: 99460
           Summary: [C++20] Template with complex non-type argument
                    re-uses different specialisation
           Product: gcc
           Version: 11.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gcc.mexon at spamgourmet dot com
  Target Milestone: ---

See below example of a Builder class.  It is templated with an array of two
booleans, and includes member functions that return an instance of a different
specialisation of the same template.  However, the different specialisations
appear to get mixed up.

The intended behaviour is that both usages in main() result in a Builder with
both booleans set.  In fact the result is that the second usage only has one
boolean set.  However, removing the first usage resolves the problem,
indicating that the problem is caused by unrelated specialisations interfering
with each other.

I checked this with gcc 9.3.0 and gcc 10.2.0.  I also tried compiling from git,
version 11.0.1 change a18ebd6c439.  Command line was "g++ -Wall --std=c++2a
builder.cpp".

More discussion and examples [on
StackOverflow](https://stackoverflow.com/questions/66520012/c-20-stdarray-as-non-type-template-argument-reshuffles-elements/66520217#66520217).

It seems likely that this is a known problem, in which case this bug report can
be closed as a duplicate.  But I would like a tracking bug to attach to the
StackOverflow question.

----

#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;
}

Reply via email to