2015-05-01 13 views
8

Hãy xem xét điều này:Tôi đang cố gắng làm tổ "map_list_of" trong C++ 03, nhưng việc xây dựng dường như không rõ ràng?

#include <iostream> 
#include <map> 
#include <string> 
#include <boost/assign/list_of.hpp> 
using boost::assign::map_list_of; 

const std::map<int, std::map<int, char> > test = map_list_of 
    (100, map_list_of 
     (1, 'a') 
     (2, 'b') 
    ) 
    (101, map_list_of 
     (1, 'c') 
     (2, 'd') 
    ) 
; 

int main() 
{ 
    std::cout << test.find(101)->second.find(2)->second << "\n"; 
} 

tôi muốn kết quả là một chương trình đó, khi thực hiện, kết quả đầu ra d.

Thay vào đó, I get this:

$ clang++ -std=c++03 -O2 -Wall -pedantic -pthread main.cpp 

In file included from main.cpp:1: 
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/iostream:39: 
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/ostream:38: 
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/ios:40: 
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/char_traits.h:39: 
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_algobase.h:64: 
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_pair.h:119:22: error: call to constructor of 'std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >' is ambiguous 
     : first(__p.first), second(__p.second) { } 
          ^ ~~~~~~~~~~ 
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_tree.h:1843:29: note: in instantiation of function template specialization 'std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > >::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >' requested here 
      _M_insert_unique_(end(), *__first); 
           ^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_map.h:255:16: note: in instantiation of function template specialization 'std::_Rb_tree<int, std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > >, std::_Select1st<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > >::_M_insert_unique<std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >' requested here 
     { _M_t._M_insert_unique(__first, __last); } 
      ^
/usr/local/include/boost/assign/list_of.hpp:163:20: note: in instantiation of function template specialization 'std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > >::map<std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >' requested here 
      return Container(begin(), end()); 
       ^
/usr/local/include/boost/assign/list_of.hpp:142:20: note: in instantiation of function template specialization 'boost::assign_detail::converter<boost::assign_detail::generic_list<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > >, std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >::convert<std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > > >' requested here 
      return convert<Container>(c, tag_type()); 
       ^
/usr/local/include/boost/assign/list_of.hpp:436:49: note: in instantiation of function template specialization 'boost::assign_detail::converter<boost::assign_detail::generic_list<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > >, std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >::convert_to_container<std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > > >' requested here 
      return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>(); 
               ^
main.cpp:7:50: note: in instantiation of function template specialization 'boost::assign_detail::generic_list<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > >::operator map<std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > > >' requested here 
const std::map<int, std::map<int, char> > test = map_list_of 
               ^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_map.h:171:7: note: candidate constructor 
     map(const _Compare& __comp, 
    ^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_map.h:182:7: note: candidate constructor 
     map(const map& __x) 
    ^
1 error generated. 

(kết quả tương tự thuộc GCC)

Làm thế nào tôi có thể giải quyết này?

Tôi gặp lỗi tương tự ngay cả khi tôi sử dụng std::map<int, char>(map_list_of(...)) thay vì map_list_of(...) cho các bản đồ bên trong đó.

+0

Nó hoạt động với "dàn diễn viên tiềm ẩn" thay vì dàn diễn viên theo chức năng. 'mẫu T implicit_cast (T t) {return t; } 'Nhưng kiểu đánh bại mục đích của' map_list_of'. (Các trình tạo phân bổ và so sánh của 'bản đồ' là' tường minh'.) – dyp

+0

@dyp: Huh, [đẹp] (http://coliru.stacked-crooked.com/a/dbcb1865c54d5f7b)! Đó là câu trả lời. Nó không đánh bại mục đích gì cả, bởi vì tôi vẫn nhận được bản đồ 'const' của tôi với bộ khởi tạo" inline ". –

+0

@dyp Tại sao công việc diễn xuất tiềm ẩn? – 0x499602D2

Trả lời

8

C++ 03 định nghĩa hai nhà xây dựng cho map có thể được gọi với một đối số [lib.map] p2:

explicit map(const Compare& comp = Compare(), 
      const Allocator& = Allocator()); 
// [...] 
map(const map<Key,T,Compare,Allocator>& x); 

tăng của map_list_of tạo ra một đối tượng của một generic_list lớp mẫu instantiation, từ SVN gần đây:

template< class Key, class T > 
inline assign_detail::generic_list< std::pair 
    < 
     BOOST_DEDUCED_TYPENAME assign_detail::assign_decay<Key>::type, 
     BOOST_DEDUCED_TYPENAME assign_detail::assign_decay<T>::type 
    > > 
map_list_of(const Key& k, const T& t) 

Trong trường hợp chính generic_list mẫu chứa các toán tử chuyển đổi sau đây:

template< class Container > 
operator Container() const 
{ 
    return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>(); 
} 

Cả map nhà xây dựng là khả thi, như toán tử này cho phép chuyển đổi cho cả hai mapCompare. Theo tôi biết, bạn không thể SFINAE-hạn chế toán tử chuyển đổi trong C++ 03.


Các map được xây dựng một cách rõ ràng khi chèn một nút mới trong ngoài bản đồ . Một cặp vòng lặp được sử dụng để lặp qua bên trong generic_list để xây dựng bên ngoài map. Dereferencing này iterator mang lại một std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> >. Loại nút (giá trị) của bản đồ ngoài là std::pair<int const, std::map<int, char> >.

Do đó, trình biên dịch sẽ cố gắng tạo kiểu sau từ trước. Trong C++ 03, hàm tạo pair này không bị ràng buộc bởi SFINAE, vì điều đó là không thể trong C++ 03. [Lib.pairs] p1

template<class U, class V> pair(const pair<U, V> &p); 

libstdC++ thực hiện điều này như sau:

template<class _U1, class _U2> 
    pair(const pair<_U1, _U2>& __p) 
    : first(__p.first), second(__p.second) { } 

Tôi không hoàn toàn chắc chắn nếu đó là tuân thủ, kể từ [lib.cặp] p4

Effects: Khởi thành viên từ các thành viên tương ứng của các đối số, thực hiện chuyển đổi tiềm ẩn khi cần thiết.

(Nhưng, như tôi đã nói, SFINAE trên ctors không thể được thực hiện trong C++ 03.)

Trong C++ 11 và 14, đây cũng thất bại, nhưng vì một lý do khác nhau. Ở đây, các nhà xây dựng ghép đôi bị ràng buộc bởi SFINAE. Tuy nhiên, hạn chế yêu cầu chuyển đổi tiềm ẩn (is_convertible), trong khi chương trình có UB nếu cặp mục tiêu của các loại không thể là được xây dựng từ các nguồn (is_constructible). Tôi đã viết thêm một chút về vấn đề này trong another SO answer. Điều thú vị là một giải pháp đề xuất N4387 đến vấn đề đề cập trong câu hỏi khác nói:

Cần lưu ý ở đây, đó là đối với trường hợp chung std::is_constructible<T, U>::value yêu cầu đối với việc không rõ ràng constructor mà bị hạn chế về std::is_convertible<U, T>::value là không cần thiết, vì nó có thể tạo ra các loại có thể được sao chép khởi tạo nhưng không trực tiếp khởi

này được chính xác trường hợp chúng tôi chạy vào đây: Một map có thể được sao chép-khởi từ a generic_list, vì điều này làm cho hàm tạo explicit không khả thi. Nhưng map không thể được khởi tạo trực tiếp từ generic_list, vì điều này làm cho chuyển đổi không rõ ràng.

Theo như tôi thấy, N4387 không giải quyết được sự cố trong OP. Mặt khác, với khởi tạo đồng bộ, chúng tôi có một thay thế cho map_list_of. Và chúng tôi có thể SFINAE hạn chế các nhà khai thác chuyển đổi kể từ C++ 11.


Một giải pháp là để loại bỏ các nhà xây dựng explicit bằng cách chỉ cho phép chuyển đổi tiềm ẩn:

template<typename T> T implicit_cast(T t) { return t; } 

implicit_cast<InnerMap>(map_list_of(1, 'a')(2, 'b')) 

Nhưng có một cách trực tiếp hơn: chỉ cần sử dụng chức năng convert_to_container thành viên của lớp cơ sở generic_list 's converter (còn mẫu lớp học):

map_list_of(1, 'a')(2, 'b').convert_to_container<InnerMap>() 
+0

Tôi sẽ không trải qua quá trình phân giải quá tải ở đây và sẽ chỉ giả định rằng giải thích của clang và gcc là chính xác và cuộc gọi thực sự mơ hồ. – dyp

+0

Vinh quang. Cảm ơn bạn! –

Các vấn đề liên quan