I'm trying to write a function, that will, for example, take an:
std::array<std::variant<int, std::string_view>, 4>
and a double d
, and return:
std::array<std::variant<int, std::string_view, double>, 5>
with d
appended to the end of the array, but I'm running into some very strange behaviour in my implementation thus far.
Right now, I have a Variant_Monoid
(with binary operation concatenate
) to handle expanding std::variant
types, which seems to be working fine:
struct Variant_Monoid {
using zero = std::monostate;
template <typename T, typename... Args>
struct [[maybe_unused]] concatenate;
template <typename... Args0, typename... Args1>
struct concatenate<std::variant<Args0...>, std::variant<Args1...>> {
using type = std::variant<Args0..., Args1...>;
};
// Convenience method to concatenate types without having to wrap them into a variant first.
template<typename... Args0, typename... Args1>
struct concatenate<std::variant<Args0...>, Args1...> {
using type = std::variant<Args0..., Args1...>;
};
};
Then I have this, where the idea is to go through the existing array, try to expand the types from the old variant to the new variant, and then append the new element:
template<size_t N, typename T, typename S>
constexpr std::array<typename Variant_Monoid::concatenate<T, S>::type, N + 1> append_element_with_type(const std::array<T, N> &a, const S &s) {
return append_element_with_type_aux(a, s, std::make_index_sequence<N>{});
}
delegating to this function:
template<size_t N, typename T, typename S, size_t... Indices>
constexpr std::array<typename Variant_Monoid::concatenate<T, S>::type, N + 1>
append_element_with_type_aux(const std::array<T, N> &a,
const S &s, std::index_sequence<Indices...>) noexcept {
return {{std::visit([](auto &&t) { return t; }, a[Indices])..., s}};
}
The behaviour of this function, though, is all over the place. In some cases (usually the first call in a series of calls), it appears to work fine. For example:
constexpr std::array<std::variant<Variant_Monoid::zero>, 0> s0{};
constexpr std::array<std::variant<Variant_Monoid::zero, int>, 1> s1 = append_element_with_type(s0, 1);
However, if I try this next, not only does the compiler complain that the expression is not constexpr
, but it fails entirely to compile with a barrage of undecipherable STL messages:
auto s2 = append_element_with_type(s1, 3.14159);
Similarly, this seems to work without issue:
constexpr std::array<std::variant<std::string_view>, 3> to_extend = {{"Hello", "there", "world"}};
constexpr std::array<std::variant<std::string_view, int>, 4> extended = append_element_with_type(to_extend, 5);
and then again, this fails with similar compilation errors and again with the claim that the expression is not constexpr
:
constexpr std::array<std::variant<std::string_view, int, double>, 5> prepended = append_element_with_type(extended, 3.14);
Any suggestions / help would be very much appreciated. I'm still fairly new to template metaprogramming and just playing around to learn more.
The call to the visitor must yield the same type and value category for all possible arguments. Explicitly specify your lambda's return type to be the new variant, i.e., typename Variant_Monoid::concatenate<T, S>::type
.