2012-06-12 9 views
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 
} 

なぜ2行目のコンパイル時にコンパイルエラーが発生するのですか? (GCC & Clangでテスト済み)空のネストされたタプルエラー

回避策はありますか?あなたがにlibstdC++のバグを発見したように打ち鳴らす

/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. 
+0

GccまたはClangのバグでしょうか?あなたのコードはVisual Studio 2010でコンパイルされます。ところで、あなたはbtを2回宣言しています、それは意図されていますか? –

+1

また、コンパイルエラーメッセージを表示できますか? –

+0

申し訳ありませんが、バギー行1はコメントアウトされました。名前を変更するのを忘れて、今編集しました。 – dunedain

答えて

12

ため

エラーメッセージが見えます! (このコードはlibC++でclangで動作します)。減少したテストケース:

#include <tuple> 

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

問題がstd::tupleがのlibstdC++で実装される方法によるものです。タプルの実装では、多重継承を伴う「再帰」が使用されます。 tuple<X, Y, Z>は、Xtuple<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>したいですか?コンパイラは知らないので、それはあきらめを選ぶ。

(あなたのケースでは、あなたが代わりにtuple<tuple<>, tuple<tuple<>>>をインスタンス化しているとして、それが原因で_Head_base<0, tuple<>>のだが、原理は同じである。)


のlibC++は同じ問題を持っていないのはなぜ?

  1. tuple<T...>のlibC++を使用組成物の代わりに、相続で__tuple_impl<...>を参照するには次の2つの主な理由があります。結果
  2. __tuple_leaf<tuple<tuple<>>>で空の基本クラスの最適化にキックしない、すなわち__tuple_leaf<tuple<tuple<>>>はしたがってtuple<tuple<>>
  3. から継承しなくなり、曖昧な基底クラスの問題は起こりません。
  4. (及び@mitchnullによって述べたように、各塩基がユニークであるが、それはここでの主な違いはない。)

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

tuple<...>代わりに組成物の継承を使用する場合、我々は、上記を参照することができるように、 OPのtuple<tuple<>, tuple<tuple<>>>はまだ__tuple_leaf<0, tuple<>>から2回継承されますが、これは問題の可能性があります。ところで

+0

libC++は非再帰的タプル実装を使用していますか? – dunedain

+0

@dunedain:再帰的。更新を参照してください。 – kennytm

+0

明確で詳細な回答に感謝します。 – dunedain

0

は、GCCを使用するために持っている人のために、私はあなたに迅速かつ汚い修正(4.8.0のために、すでにバグレポートを提出)与えてみましょう:

をソリューションは、小規模な変更です

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; 
:>タプル<に空のベースの最適化を防ぐために、タプル実装における__empty_not_finalタイプの210

(これはタプル<>型のアドホックな解決策に過ぎません。KennyTMで記述されている実際の問題は解決しません。つまり、struct A{}; auto d = std::tuple<std::tuple<std::tuple<A, A>, A>, A>{};はまだコンパイルされません)

関連する問題