Путаница при выводе из std:: кортеж, не может обрабатывать std:: get


Моя основная идея состояла в том, чтобы вывести мой собственный класс из std:: tuple, чтобы получить некоторые вспомогательные типы внутри, как это:

template <typename ... T>
class TypeContainer: public std::tuple<T...>
{   
    public:
        using BaseType = std::tuple<T...>;
        static const size_t Size = sizeof...(T);
        TypeContainer(T... args):std::tuple<T...>(args...){};

        using index_sequence = std::index_sequence_for<T...>;
};

Теперь я пытаюсь использовать код следующим образом:

using MyType_tuple_with_empty =         std::tuple<       std::tuple<float,int>,    std::tuple<>,    std::tuple<int>>;
using MyType_typecontainer_with_empty = TypeContainer< TypeContainer<float,int>, TypeContainer<>, TypeContainer<int>>;

using MyType_tuple_non_empty =          std::tuple<       std::tuple<float,int>,    std::tuple<int>,    std::tuple<int>>;
using MyType_typecontainer_non_empty =  TypeContainer< TypeContainer<float,int>, TypeContainer<int>, TypeContainer<int>>;

template <typename T>
void Do( const T& parms )
{
    // The following lines result in errors if TypeContainer with
    // empty element comes in. The empty element is in std::get<1> which
    // is NOT accessed here!
    std::cout << std::get<0>(std::get<0>(parms)) << " ";
    std::cout << std::get<1>(std::get<0>(parms)) << std::endl;

    std::cout << std::get<0>(std::get<2>(parms)) << std::endl;
}


int main()
{
    MyType_tuple_with_empty         p1{{ 1.2,3},{},{1}};
    Do( p1 );

    MyType_typecontainer_with_empty p2{{ 1.2,3},{},{1}};
    Do( p2 ); // << this line raise the error

    MyType_tuple_non_empty          p3{{ 1.2,3},{4},{1}};
    Do( p3 );

    MyType_typecontainer_non_empty  p4{{ 1.2,3},{4},{1}};
    Do( p4 );
}

Если я компилирую с Do(p2), я получаю следующую ошибку:

Ошибка: нет соответствующей функции для вызова 'get(const TypeContainer<TypeContainer<float, int>, TypeContainer<>, TypeContainer<int> >&)'

Может ли кто-нибудь объяснить, почему существование пустого TypeContainer в связи с std::get приведет к этой проблеме?

Править: Дополнительная информация:

В строки

MyType_tuple_with_empty         p1{{{ 1.2,3},{},{1}}};
MyType_tuple_non_empty          p3{{ 1.2,3},{4},{1}};

Может быть скомпилирован не с gcc5. 2. 0, а с gcc6.1.0. Это немного загадочно, потому что я помню, что конструктор кортежа действительно явный. Почему это работает с gcc6.1.0? Но это не та проблема, которую я ищу: -)

Еще один намек: Код, с которым у меня возникли проблемы, похоже, компилируется с clang3. 5. 0.

Немного трудно понять...

Edit2: Копаясь в списках ошибок (длинный: -)) я нашел:

/опт/Линукс-gnu_5.2.0/включать/с++/5.2.0/кортеж|832 коль 5| Примечание: аргумент шаблона вычета/замещения не: главный.cpp / 104 col 45 / Примечание: 'std::tuple<_Elements ...> ' является неоднозначным базовым классом 'TypeContainer<TypeContainer<float, int>, TypeContainer<>, TypeContainer<int> >' || std::cout << std::get<0>(std::get<0>(parms)) << " ";

Похоже, что в libg++ кто-то извлекает несколько раз из любого типа кортежа, который кажется сломанной библиотекой. Поиск по этой теме приводит меня к следующему: ошибка пустых вложенных кортежей

Это действительно связано? Тот же баг или новый: -)

1 7

1 ответ:

К сожалению, вам придется добавить свои контейнерные версии функций get:

template <std::size_t I, typename ...T>
decltype(auto) get(TypeContainer<T...>&& v)
{
    return std::get<I>(static_cast<std::tuple<T...>&&>(v));
}
template <std::size_t I, typename ...T>
decltype(auto) get(TypeContainer<T...>& v)
{
    return std::get<I>(static_cast<std::tuple<T...>&>(v));
}
template <std::size_t I, typename ...T>
decltype(auto) get(TypeContainer<T...> const& v)
{
    return std::get<I>(static_cast<std::tuple<T...> const&>(v));
}

И просто использовать get Не std::get в Do вид функций. Компилятор может выбрать пространство имен из аргументов.

Я думаю, я не уверен, что это потому, что gcc имеет EBO - пустую базовую оптимизацию - реализованную в своих кортежах. Какова именно причина, догадаться довольно трудно. Вы можете сообщить об этом в gcc bugzilla.


Кстати, это не очень хорошая привычка производные от классов STD. Если бы вы начали с компиляции, а не наследования, то вам нужно было бы предоставить свои собственные функции get, и вы не заметили бы этой ошибки, экономя, вероятно, много времени.