#c #gcc #visual-c #clang
Вопрос:
Поэтому недавно я заинтересовался созданием кортежа с тегами, который является кортежем, но вы можете использовать функцию get с тегами вместо индексов. Код довольно длинный, так что со мной все просто.
// tagged_tuple.hpp // simple class to hold two types // works with incomplete types, important for our use case template lt;class First, class Secondgt; struct type_pair { using first = First; using second = Second; }; namespace detail { // gets info about a classes name tag (key -gt; value type mapping) from the class template lt;class Tgt; struct name_tag_traits { // aliases for our specific use case using tag_type = typename T::first; using value_type = typename T::second; }; // helper alias, turns a list of TypePairs supplied to tagged_tuple to a list of // key/tag/name types template lt;class Tgt; using name_tag_t = typename name_tag_traitslt;Tgt;::tag_type; // same as above but returns a list of value types template lt;class Tgt; using name_tag_value_t = typename name_tag_traitslt;Tgt;::value_type; template lt;class Tgt; struct Base {}; template lt;class... Tsgt; struct TypeSet : Baselt;Tsgt;... { template lt;class Tgt; constexpr auto operator (Baselt;Tgt;) { if constexpr (std::is_base_of_vlt;Baselt;Tgt;, TypeSetgt;) { return TypeSet{}; } else { return TypeSetlt;Ts..., Tgt;{}; } } constexpr auto size() const -gt; std::size_t { return sizeof...(Ts); } }; // checks if tags are unique template lt;class... Typesgt; constexpr auto are_tags_unique() -gt; bool { constexpr auto typeset = (TypeSetlt;gt;{} ... Baselt;name_tag_tlt;Typesgt;gt;{}); return typeset.size() == sizeof...(Types); } template lt;class Needlegt; constexpr auto index_of_impl(size_t index, size_t end) -gt; std::size_t { return end; }; template lt;class Needle, class T, class... Haystackgt; constexpr auto index_of_impl(size_t index, size_t end) -gt; std::size_t { return std::is_samelt;Needle, Tgt;::value ? index : index_of_impllt;Needle, Haystack...gt;(index 1, end); }; // find the index of T in a type list, returns sizeof...(Haystack) 1 on failure (think std::end()) template lt;class Needle, class... Haystackgt; static constexpr auto index_of() -gt; std::size_t { return index_of_impllt;Needle, Haystack...gt;(0, sizeof...(Haystack) 1); }; }; // namespace detail // and here's our little wrapper class that enables tagged tuples template lt;class... TypePairsgt; class tagged_tuple : public std::tuplelt;detail::name_tag_value_tlt;TypePairsgt;...gt; { public: // throws an error if tags are not unique static_assert(detail::are_tags_uniquelt;TypePairs...gt;(), "Duplicated tags!"); // not really needed for now but if we switch to private inheritance it'll come in handy using tag_type = std::tuplelt;detail::name_tag_tlt;TypePairsgt;...gt;; using value_type = std::tuplelt;detail::name_tag_value_tlt;TypePairsgt;...gt;; using value_type::value_type; using value_type::swap; using value_type::operator =; }; // our special get functions template lt;class Name, class... TypePairsgt; auto get(tagged_tuplelt;TypePairs...gt;amp; tuple) -gt; typename std::tuple_elementlt; detail::index_oflt;Name, detail::name_tag_tlt;TypePairsgt;...gt;(), typename tagged_tuplelt;TypePairs...gt;::value_typegt;::typeamp; { return std::getlt;detail::index_oflt;Name, detail::name_tag_tlt;TypePairsgt;...gt;()gt;( tuple); }; template lt;class Name, class... TypePairsgt; auto get(const tagged_tuplelt;TypePairs...gt;amp; tuple) -gt; const typename std::tuple_elementlt; detail::index_oflt;Name, detail::name_tag_tlt;TypePairsgt;...gt;(), typename tagged_tuplelt;TypePairs...gt;::value_typegt;::typeamp; { return std::getlt;detail::index_oflt;Name, detail::name_tag_tlt;TypePairsgt;...gt;()gt;( tuple); }; template lt;class Name, class... TypePairsgt; auto get(tagged_tuplelt;TypePairs...gt;amp;amp; tuple) -gt; typename std::tuple_elementlt; detail::index_oflt;Name, detail::name_tag_tlt;TypePairsgt;...gt;(), typename tagged_tuplelt;TypePairs...gt;::value_typegt;::typeamp;amp; { return std::getlt;detail::index_oflt;Name, detail::name_tag_tlt;TypePairsgt;...gt;()gt;( std::move(tuple)); };
И синтаксис был бы:
// main.cpp using foo = tagged_tuplelt;type_pairlt;class bar, intgt;, type_pairlt;class baz, std::stringgt;gt;; foo a{ 1, "foo" }; std::cout lt;lt; getlt;bargt;(a) lt;lt; " " lt;lt; getlt;bazgt;(a);
Выход:
1 foo
Все шло нормально, пока я не протестировал этот фрагмент кода:
std::tuplelt;int, std::stringgt; a{ 1, "foo" }; foo b{ a };
что, по моей теории, должно сработать, поскольку tagged_tuple является производным от std::tuple и для этого случая имеет конструктор. Затем я протестировал в Visual Studio 2019, и это полностью сработало. Но когда я пытаюсь использовать gcc/clang, я получаю эту ошибку:
error: no matching constructor for initialization of 'foo' (aka 'tagged_tuplelt;type_pairlt;class bar, intgt;, type_pairlt;class baz, std::stringgt;gt;') foo b{ a };
Что странно, так как я уже включил все конструкторы базового класса using value_type::value_type
(насколько мне известно). Затем я добавил следующий конструктор:
template lt;class... Typesgt; constexpr tagged_tuple(std::tuplelt;Types...gt; amp;tuple) : value_type(tuple) {}
И на этот раз это сработало. Я не могу понять, почему это не сработало бы без этого фрагмента кода.
Есть ли ошибка в gcc/clang или я что-то упускаю?
Комментарии:
1. Он компилируется , если я изменю его на
using typename value_type::tuple;
, но я не думал, что вам это нужноtypename
. Может быть, вам подойдет псевдоним.2. Обновление: Похоже, что Visual Studio 2022 также не работает. Может быть, это просто вопрос ПРОТИВ 2019 года?
3. @chris я перешел на это, и на этот раз лязг сработал, но gcc этого не сделал. Что, черт возьми, здесь происходит
4. Я готов поспорить, что вы обнаружили по крайней мере одну ошибку компилятора, как это может произойти с тяжелыми шаблонами, но это также может быть что-то вроде IFNDR, где все компиляторы будут правильными.
5. Вы не можете наследовать конструкторы копирования, поэтому преобразование из другого типа может зависеть от сведений об одном из конструкторов
tuple
.