Почему этот код работает с c 17, но не работает с c 20 (MSVC)?

#c #c 17 #metaprogramming #constexpr #c 20

#c #c 17 #метапрограммирование #constexpr #c 20

Вопрос:

У меня есть constexpr_sequence класс, который возвращает новое значение при каждом next() вызове и текущее значение при value() вызове в зависимости от функции и начального значения, заданного в качестве параметров шаблона. Например:

 template<typename T, T INC>
constexpr T increase(T val)
{
    return val   INC;
}

using counter = qx::constexpr_sequence<struct Tag, int, 0, increase<int, 1>>;
static_assert(counter::value() == 0);
static_assert(counter::next()  == 1);
static_assert(counter::value() == 1);
static_assert(counter::value() == 1);
static_assert(counter::next()  == 2);
static_assert(counter::value() == 2);
static_assert(counter::next()  == 3);
static_assert(counter::value() == 3);
 

Этот код отлично работает с MSVC, Clang, Apple Clang и GCC, а также с C 17, но тесты для MSVC завершаются неудачно, когда я включаю C 20 ( counter::value() всегда возвращает 0, несмотря на то, что counter::next() DO изменяет соответствующий флаг.

Код класса:

 template <class Tag, typename T, T Start, T Func(T)>
class constexpr_sequence
{
private:

template <T nIndex>
struct Element
{
    static constexpr T value(void) noexcept
    {
        T _value = Start;

        for (std::size_t i = 0; i < nIndex; i  )
            _value = Func(_value);

        return _value;
    }
};

template<std::size_t nCurrent, bool bWasSetted /* = false */>
struct CheckerSetter
{
    static constexpr std::size_t index(void) noexcept
    {
        return nCurrent;
    }
};

template<T nCurrent>
struct CheckerWrapper
{
    template<bool        bWasSetted = constexpr_flag<Element<nCurrent>>{}.test(),
             std::size_t nNext      = CheckerSetter<nCurrent, bWasSetted>{}.index()>
    static constexpr std::size_t index(void) noexcept
    {
        return nNext;
    }
};

template<std::size_t nCurrent>
struct CheckerSetter<nCurrent, /* bool bWasSetted = */ true>
{
    template<std::size_t nNext = CheckerWrapper<nCurrent   1>{}.index()>
    static constexpr std::size_t index(void) noexcept
    {
        return nNext;
    }
};

public:

template <std::size_t nIndex = CheckerWrapper<0>{}.index(),
          T           _value = Element<nIndex>{}.value()>
static constexpr T value(void) noexcept
{
    return _value;
}

template <std::size_t nIndex = CheckerWrapper<0>{}.index(),
          T           _value = Element<nIndex>{}.value(),
          bool        bStub  = constexpr_flag<Element<nIndex>>{}.test_and_set()>
static constexpr T next(void) noexcept
{
    return Func(_value);
}

};
 

Класс имеет мой constexpr_flag класс как зависимость, этот класс возвращает «Start» в начале ( test() ), «Start» с test_and_set и «End» после test() . Тесты для этого кода проходят успешно, но вы можете проверить это, если считаете, что проблема именно здесь.

репо:
constexpr_flag
constexpr_sequence
test_constexpr_flag
тест_constexpr_sequence

Чтобы воспроизвести проблему:

 git clone https://github.com/n0lavar/qxLib.git
open qxLibcmakeplatform_options.cmake, change cxx_std_17 to cxx_std_20

git clone https://github.com/google/googletest.git
cd googletest
mkdir build
cd build
cmake ..
cmake --build .
cd ..
cd ..

cd qxLib
mkdir build
cd build
cmake -DGENERATE_TESTS=1 ..
cmake --build .
 

сборка завершится ошибкой с test_constexpr_sequence (и зависит от них test_constexpr_random и test_rtti )

Вопросы:
Почему этот код не работает?
Правильно ли поведение MSVC (было бы здорово увидеть ссылки на стандарт)?
Какие изменения мне нужно внести, чтобы исправить это?

Комментарии:

1. Для меня это constexpr_sequence подозрительно. Как он должен работать, если он используется в нескольких единицах перевода?

2. Он был разработан для целей идентификаторов классов, он отлично работал для меня с c 17. В любом случае, ваш вопрос не о реальной проблеме

3. Почему этот код не работает? Правильно ли поведение MSVC (было бы здорово увидеть ссылки на стандарт)? Какие изменения мне нужно внести, чтобы исправить это? — Может быть, компилятор сломан? Если компилятор продемонстрировал такое поведение, не следует ли первым делом перейти на сайт Microsoft и показать им свои результаты? Я уверен, что инженеры там исправят (если сломано) как можно скорее. Если компилятор сломан, они были бы теми, кто мог бы это исправить — никто здесь не смог бы этого сделать.

4. Ошибка, о которой сообщается как о возможной ошибке компилятора developercommunity.visualstudio.com/content/problem/1300886 /…