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

Tại sao Dòng 1 đưa ra lỗi biên dịch trong khi Dòng 2 biên dịch tốt? (được kiểm tra ở cả Gcc & Clang)Lỗi tuples lồng nhau trống

Có cách giải quyết nào khác không?

thông báo lỗi cho vang

/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

Có thể là lỗi trong Gcc hoặc Clang không? Mã của bạn biên dịch với Visual Studio 2010. Bằng cách này, bạn đang khai báo bt hai lần, đó là dự định? –

+1

Ngoài ra, bạn có thể chỉ ra thông báo lỗi biên dịch không? –

+0

xin lỗi, Dòng 1 bị lỗi đã được nhận xét. Tôi quên thay đổi tên, chỉnh sửa ngay bây giờ. – dunedain

Trả lời

12

Hình như bạn tìm thấy một lỗi trong libstdC++! (Mã này hoạt động trong clang với libC++). Một trường hợp kiểm tra giảm:

#include <tuple> 

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

Vấn đề là do cách std::tuple được thực hiện trong libstdC++. Việc thực hiện tuple sử dụng "đệ quy" với nhiều thừa kế. Bạn có thể nghĩ đến số tuple<X, Y, Z> là kế thừa từ cả hai số Xtuple<Y, Z>. Điều này có nghĩa là tuple<tuple<>> sẽ được kế thừa từ cả hai số tuple<>tuple<> và điều đó sẽ gây ra lỗi cơ sở không rõ ràng. Tất nhiên vấn đề thực sự không giống như vậy, bởi vì tuple<tuple<>> không tạo ra lỗi nào.

Việc thực hiện thực sự mà gây ra lỗi là như thế này:

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...> {}; 

Khi chúng tôi nhanh chóng tuple<tuple<tuple<>>>, chúng tôi nhận phân cấp thừa kế này:

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

Chúng ta thấy rằng _Tuple_impl<1> có thể truy cập vào hai những con đường khác nhau. Đây chưa phải là vấn đề, vấn đề nằm trong constructor di chuyển, người gọi hàm khởi tạo chuyển đổi là _Tuple_impl<1>. Bạn muốn dùng _Tuple_impl<1> nào? Trình biên dịch không biết, do đó, nó chọn bỏ cuộc.

(Trong trường hợp của bạn đó là vì _Head_base<0, tuple<>> như bạn đang instantiating tuple<tuple<>, tuple<tuple<>>> thay vào đó, nhưng nguyên tắc là như nhau.)


Tại sao ++ libc không có cùng một vấn đề? Có hai lý do chính:

  1. tuple<T...> trong libC++ sử dụng thành phần thay vì thừa kế để tham chiếu đến __tuple_impl<...>.
  2. Kết quả là, trống tối ưu hóa lớp cơ sở trong __tuple_leaf<tuple<tuple<>>> không kick vào, ví dụ: __tuple_leaf<tuple<tuple<>>> sẽ không kế thừa từ tuple<tuple<>>
  3. Do đó, vấn đề lớp cơ sở không rõ ràng sẽ không xảy ra.
  4. (và mỗi cơ sở là duy nhất như đã đề cập bởi @mitchnull, nhưng đó không phải là một sự khác biệt chính ở đây.)

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

Như chúng ta đã thấy ở trên, nếu tuple<...> sử dụng thừa kế thay vì thành phần, OP2 tuple<tuple<>, tuple<tuple<>>> vẫn sẽ kế thừa từ __tuple_leaf<0, tuple<>> hai lần, có thể là một vấn đề.

+0

Tôi thấy, không libC++ sử dụng một thực hiện tuple nonrecursive? – dunedain

+0

@dunedain: đệ quy. Xem cập nhật. – kennytm

+0

cảm ơn bạn đã trả lời rõ ràng và chi tiết. – dunedain

0

Bằng cách này, đối với những người phải sử dụng gcc, hãy để tôi cung cấp cho bạn một cách nhanh chóng và bẩn sửa chữa (đối với 4.8.0, đã đệ trình một báo cáo lỗi):

Giải pháp là một thay đổi nhỏ của __empty_not_final trong việc thực hiện tuple, để ngăn chặn tối ưu hóa cơ sở có sản phẩm nào cho tuple <> loại:

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

thay vì

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

(Lưu ý rằng, đây chỉ là giải pháp adhoc cho tuple <> loại, nó không giải quyết được vấn đề thực sự được mô tả bởi KennyTM, tức là struct A{}; auto d = std::tuple<std::tuple<std::tuple<A, A>, A>, A>{}; vẫn không biên dịch)