Вопрос по nested, c++11, tuples, c++ – Ошибка пустых вложенных кортежей

6
#include <iostream>
#include <tuple>
int main(){

auto bt=std::make_tuple(std::tuple<>(),std::tuple<std::tuple<>>()); //Line 1
auto bt2=std::make_tuple(std::tuple<>(),std::tuple<>());             //Line 2
}

Почему строка 1 выдает ошибку компиляции, а строка 2 компилируется нормально? (проверено как в Gcc, так и в Clang)

Есть ли возможное решение?

сообщение об ошибке для лязга

/usr/include/c++/4.6/tuple:150:50: error: ambiguous conversion from derived class 'std::_Tuple_impl<0, std::tuple<>,
      std::tuple<std::tuple<> > >' to base class 'std::_Head_base<0, std::tuple<>, true>':
    struct std::_Tuple_impl<0, class std::tuple<>, class std::tuple<class std::tuple<> > > -> _Tuple_impl<0UL + 1, class std::tuple<class std::tuple<> > > -> _Head_base<1UL, class std::tuple<class std::tuple<> >, std::is_empty<class tuple<class tuple<> > >::value> -> class std::tuple<class std::tuple<> > -> _Tuple_impl<0, class std::tuple<> > -> _Head_base<0UL, class std::tuple<>, std::is_empty<class tuple<> >::value>
    struct std::_Tuple_impl<0, class std::tuple<>, class std::tuple<class std::tuple<> > > -> _Head_base<0UL, class std::tuple<>, std::is_empty<class tuple<> >::value>
      _Head&            _M_head()       { return _Base::_M_head(); }
                                                 ^~~~~
/usr/include/c++/4.6/tuple:173:33: note: in instantiation of member function 'std::_Tuple_impl<0, std::tuple<>,
      std::tuple<std::tuple<> > >::_M_head' requested here
        _Base(std::forward<_Head>(__in._M_head())) { }
                                       ^
/usr/include/c++/4.6/tuple:334:9: note: in instantiation of member function 'std::_Tuple_impl<0, std::tuple<>,
      std::tuple<std::tuple<> > >::_Tuple_impl' requested here
      : _Inherited(static_cast<_Inherited&&>(__in)) { }
        ^
gcc_bug.cpp:5:10: note: in instantiation of member function
      'std::tuple<std::tuple<>, std::tuple<std::tuple<> > >::tuple' requested here
        auto bt=std::make_tuple(std::tuple<>(),std::tuple<std::tuple<>>());
                ^
1 error generated.
Кроме того, не могли бы вы указать сообщение об ошибке компиляции? Julien Lebot
извините, глючная строка 1 была закомментирована. Я забыл изменить имя, отредактировано сейчас. dunedain
Может ли это быть ошибка в Gcc или Clang? Ваш код компилируется с Visual Studio 2010. Кстати, вы объявляете bt дважды, это предназначено? Julien Lebot
скорее ошибка в libstdc ++. попробуйте с libc ++, если можете. mitchnull
@ecatmur: не совсем. Код в вашей ссылке прекрасно компилируется в gcc 4.7 с libstdc ++, а код OP - нет. kennytm

Ваш Ответ

2   ответа
0

для тех, кто должен использовать GCC, позвольте мне дать вам быстрый иdirty исправить (для 4.8.0, уже отправлено сообщение об ошибке):

Решение представляет собой небольшую модификацию __empty_not_final в реализации кортежа, чтобы предотвратить оптимизацию пустой базы для кортежа & lt; & gt; тип :

template<typename _Tp>
    using __empty_not_final
      = typename conditional<__is_final(_Tp)||is_same<_Tp,tuple<>>::value,
false_type, is_empty<_Tp>>::type;

вместо

template<typename _Tp>
    using __empty_not_final
      = typename conditional<__is_final(_Tp), false_type, is_empty<_Tp>>::type;

(Обратите внимание, что это только временное решение для типа tuple & gt ;, оно не решает реальную проблему, описанную KennyTM, т.е.struct A{}; auto d = std::tuple<std::tuple<std::tuple<A, A>, A>, A>{}; все равно не компилируется)

12

Похоже, вы нашли ошибку в libstdc ++! (Этот код работает в clang с libc ++). Сокращенный контрольный пример:

#include <tuple>

int main(){
    auto b = std::tuple<std::tuple<std::tuple<>>>{};
}

Проблема связана с тем, какstd::tuple реализовано в libstdc ++. Реализация кортежа использует "рекурсию" с множественным наследованием. Вы можете думать оtuple<X, Y, Z> как наследование от обоихX а такжеtuple<Y, Z>, Это означаетtuple<tuple<>> унаследует от обоихtuple<> а такжеtuple<> и это приведет к неоднозначной базовой ошибке. Конечно, настоящая проблема не такова, потому чтоtuple<tuple<>> не выдает никакой ошибки.

Реальная реализация, которая вызвала ошибку, выглядит так:

template<size_t _Idx, typename _Head>
struct _Head_base : public _Head
{};

template<size_t _Idx, typename... _Elements>
struct _Tuple_impl;

template<size_t _Idx>
struct _Tuple_impl<_Idx> {};

template<size_t _Idx, typename _Head, typename... _Tail>
struct _Tuple_impl<_Idx, _Head, _Tail...>
    : public _Tuple_impl<_Idx + 1, _Tail...>,
      private _Head_base<_Idx, _Head>
{
    typedef _Tuple_impl<_Idx + 1, _Tail...> _Inherited;
    constexpr _Tuple_impl() = default;
    constexpr _Tuple_impl(_Tuple_impl&& __in) : _Inherited(std::move(__in)) {}
};

template<typename... _Elements>
struct tuple : public _Tuple_impl<0, _Elements...> {};

Когда мы создаемtuple<tuple<tuple<>>>мы получаем эту иерархию наследования:

inheritance diagram of <code>tuple<tuple<tuple<>>></code> in libstdc++

Мы видим, что_Tuple_impl<1> достижим в двух разных направлениях. Это еще не проблема, проблема в конструкторе перемещения, который вызывает конструктор преобразования преобразования_Tuple_impl<1>, Который_Tuple_impl<1> вы хотите? Компилятор не знает, поэтому он выбирает отказ.

(В вашем случае это из-за_Head_base<0, tuple<>> как вы создаетеtuple<tuple<>, tuple<tuple<>>> вместо этого, но принцип тот же.)


Почему libc ++ не имеет такой же проблемы? Есть две основные причины:

  1. tuple<T...> in libc++ use composition instead of inheritance to refer to __tuple_impl<...>.
  2. As a result, the empty base class optimization in __tuple_leaf<tuple<tuple<>>> does not kick in, i.e. __tuple_leaf<tuple<tuple<>>> won't inherit from tuple<tuple<>>
  3. Therefore, the ambiguous base class problem won't happen.
  4. (and each base is unique as mentioned by @mitchnull, but that is not a main difference here.)

inheritance diagram of <code>tuple<tuple<tuple<>>></code> in libc++

Как мы видим выше, еслиtuple<...> использует наследование вместо композиции, OPtuple<tuple<>, tuple<tuple<>>> будет по-прежнему наследовать от__tuple_leaf<0, tuple<>> дважды, что может быть проблемой.

спасибо за ясность & amp; подробный ответ. dunedain
@mitchnull: Да, вы правы, я пропустил... :)
На самом деле libc ++ также использует множественное наследование в реализации, но оно «плоское», и каждая база уникальна. Для деталей, вы можете проверить мой пост об этом наmitchnull.blogspot.com/2012/06/….
Я вижу, использует ли libc ++ нерекурсивную реализацию кортежей? dunedain
@dunedain: рекурсивный. Смотрите обновление.

Похожие вопросы