首页 > 解决方案 > 为什么此代码适用于 c++17,但不适用于 c++20 (MSVC)?

问题描述

我有一个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,但是当我启用 C++20 时 MSVC 测试失败(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类作为依赖项,该类在开头(test())处返回“开始”,在 之后返回“开始”test_and_set和“结束” test()。此代码的测试成功通过,但如果您认为问题出在此处,您可以检查它。

回购:
constexpr_flag
constexpr_sequence
test_constexpr_flag
test_constexpr_sequence

要重现问题:

git clone https://github.com/n0lavar/qxLib.git
open qxLib\cmake\platform_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_randomtest_rtti

问题:
为什么这段代码不起作用?
MSVC 的行为是否正确(看到对标准的引用会很酷)?
我需要进行哪些更改才能修复它?

标签: c++c++17metaprogrammingconstexprc++20

解决方案


推荐阅读