Clang 对我的代码有什么问题? GCC 和 MSVC 认为没问题

What's Clang's problem with my code? GCC and MSVC think it's fine

我正在参加 Advent of Code 挑战(2021 年第 18 天)。就像测试一样,我尝试在不同的编译器上编译它。虽然 GCC (11.2) 和 MSVC (19.30) 认为没问题,但 Clang (13.0.0) 会抛出错误列表。 link to compiler explorer

/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/alloc_traits.h:514:4: error: no matching function for call to 'construct_at'
          std::construct_at(__p, std::forward<_Args>(__args)...);
          ^~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/vector.tcc:115:21: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<RegularNumber>>::construct<RegularNumber, int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>' requested here
            _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                           ^
<source>:115:19: note: in instantiation of function template specialization 'std::vector<RegularNumber>::emplace_back<int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>' requested here
        regNumVec.emplace_back(*it - '0', leftRegNumIdx,
                  ^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_construct.h:94:5: note: candidate template ignored: substitution failure [with _Tp = RegularNumber, _Args = <int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>]: no matching constructor for initialization of 'RegularNumber'
    construct_at(_Tp* __location, _Args&&... __args)
    ^

为什么 Clang 不理解其他两个看起来不错的东西?这是一个错误,还是我的错误?

这是“最小化”代码:

#include <algorithm>
#include <concepts>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

template <typename Type, typename Description>
class Named {
   public:
    using UnderlyingType = Type;

    // clang-format off
  constexpr Named()
    noexcept(std::is_nothrow_default_constructible_v<Type>)
    requires std::default_initializable<Type>
  = default;
  explicit constexpr Named(Type const &val)
    noexcept(std::is_nothrow_copy_constructible_v<Type>) 
    requires std::copyable<Type>
    : val_{val}
  {}
  explicit constexpr Named(Type&& val)
    noexcept(std::is_nothrow_move_constructible_v<Type>)
    requires std::movable<Type>
    : val_{std::move(val)}
  {}

  constexpr operator Type() const&
    noexcept(std::is_nothrow_copy_constructible_v<Type>)
    requires std::copyable<Type>
  {
    return val_;
  }
  constexpr operator Type() const&&
    noexcept(std::is_nothrow_move_constructible_v<Type>) // move assignable?
    requires std::movable<Type>
  {
    return std::move(val_);
  }

 private:
  Type val_;
};
// clang-format on

template <typename T>
auto readinputdata(std::istream &&is) {
    auto data{std::vector<T>{}};
    copy(std::istream_iterator<T>{is}, std::istream_iterator<T>{},
         back_inserter(data));
    return data;
}

static constexpr auto testData{
    "[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]\n"
    "[[[5,[2,8]],4],[5,[[9,9],0]]]\n"
    "[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]\n"
    "[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]\n"
    "[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]\n"
    "[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]\n"
    "[[[[5,4],[7,7]],8],[[8,3],8]]\n"
    "[[9,3],[[9,9],[6,[4,9]]]]\n"
    "[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]\n"
    "[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]\n"};

struct index_for_vector_of_Pair;
using PairIndex = Named<int, index_for_vector_of_Pair>;

struct index_for_vector_of_RegularNumber;
using RegNumIndex = Named<int, index_for_vector_of_RegularNumber>;

struct RegularNumber {
    using IndexType = RegNumIndex;

    int value{};
    RegNumIndex leftIdx{}, rightIdx{};
};

using IndexVariant = std::variant<PairIndex, RegNumIndex>;

struct Pair {
    using IndexType = PairIndex;

    PairIndex parent{};
    IndexVariant leftIdx{}, rightIdx{};
};

static auto pairVec{std::vector<Pair>{}};
static auto regNumVec{std::vector<RegularNumber>{}};

// used for converting the input strings to pairs
static auto leftRegNumIdx{RegNumIndex{-1}};

static constexpr auto invalid_index{-1};

template <typename Type>
auto get_last_index(std::vector<Type> const &vec) {
    using Index = typename Type::IndexType;
    return Index{static_cast<typename Index::UnderlyingType>(size(vec))};
}

auto generate_pair(std::string::const_iterator &it, PairIndex parent)
    -> PairIndex;

auto generate_side(std::string::const_iterator &it, PairIndex pairIdx)
    -> IndexVariant {
    if (*it == '[') {
        return generate_pair(it, pairIdx);
    } else {
        regNumVec.emplace_back(*it - '0', leftRegNumIdx, // error on this line
                               RegNumIndex{invalid_index});
        auto const litIdx{get_last_index(regNumVec)};
        if (leftRegNumIdx != invalid_index) {
            regNumVec[leftRegNumIdx].rightIdx = litIdx;
        }
        leftRegNumIdx = litIdx;
        return litIdx;
    };
}

auto generate_pair(std::string::const_iterator &it,
                   PairIndex parent = PairIndex{invalid_index}) -> PairIndex {
    pairVec.emplace_back();
    auto const pairIdx{get_last_index(pairVec)};
    auto &pair{pairVec.back()};
    pair.parent = parent;

    ++it;  // skip [
    pair.leftIdx = generate_side(it, pairIdx);
    ++it;  // skip comma
    pair.rightIdx = generate_side(it, pairIdx);
    ++it;  // skip ]

    return pairIdx;
}

int main() {
    auto const snailFishNumbers{readinputdata<std::string>(  //
        std::stringstream{testData}                          //
        )};

    auto const outerPairIdxs{[&] {
        auto outerPairIdxs{std::vector<PairIndex>{}};
        outerPairIdxs.reserve(size(snailFishNumbers));
        transform(cbegin(snailFishNumbers), cend(snailFishNumbers),
                  back_inserter(outerPairIdxs), [](auto const &str) {
                      auto it{cbegin(str)};  // need lvalue
                      return generate_pair(it);
                  });
        return outerPairIdxs;
    }()};

    return 0;
}

您的类型是聚合

struct RegularNumber {
    using IndexType = RegNumIndex;

    int value{};
    RegNumIndex leftIdx{}, rightIdx{};
};

其中 construct_at 尝试用圆括号初始化

::new(std::declval<void*>()) T(std::declval<Args>()...).

但是,聚合需要 {} 初始化器,直到编译器实现 P0960 Allow initializing aggregates from a parenthesized list of values

而且 Clang 还没有出现。 Compiler support for C++20