6

I'm working on an Advent of Code challenge (2021 day 18). Just as a test I tried compiling it on different compilers. While GCC (11.2) and MSVC (19.30) think it's fine, Clang (13.0.0) throws a list of errors. 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)
    ^

What doesn't Clang understand what seems fine according to the other two? Is this a bug, or my mistake?

Here's the "minimalized" code:

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

1 Answer 1

13

Your type is an aggregate

struct RegularNumber {
    using IndexType = RegNumIndex;

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

which construct_at tries to initialize with round parenthesis

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

However, an aggregate needs a {} initializer, until the compiler implements P0960 Allow initializing aggregates from a parenthesized list of values

And Clang just isn't there yet. Compiler support for C++20

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.