I am trying to embed boost "map_list_of" in C ++ 03, but apparently the construction is ambiguous?
Consider this:
#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"; } I wanted the result to be a program that outputs d when executed.
Instead, 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. (similar results in GCC)
How can i solve this?
I get a similar error even if for these internal maps I use std::map<int, char>(map_list_of(...)) instead of map_list_of(...) .
C ++ 03 defines two constructors for map that can be called with a single [lib.map] p2 argument:
explicit map(const Compare& comp = Compare(), const Allocator& = Allocator()); // [...] map(const map<Key,T,Compare,Allocator>& x); boost map_list_of creates an instance object of the generic_list class, from a recent SVN:
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 ) If the main generic_list template contains the following conversion operator:
template< class Container > operator Container() const { return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>(); } Both map constructors are viable since this operator can be converted to both map and Compare . As far as I know, you cannot SFINAE-restrict the conversion operator in C ++ 03.
map is created explicitly when a new node is inserted into an external map. A pair of iterators is used to iterate over an internal generic_list to build an external map . Declaring this iterator gives a std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > . The node type (value) of the external mapping std::pair<int const, std::map<int, char> > .
Therefore, the compiler is trying to build the last type from the first. In C ++ 03, this pair constructor is not limited to SFINAE, since this is not possible in C ++ 03. [Lib.pairs] p1
template<class U, class V> pair(const pair<U, V> &p); libstdC ++ implements this as follows:
template<class _U1, class _U2> pair(const pair<_U1, _U2>& __p) : first(__p.first), second(__p.second) { } I'm not quite sure if this is compatible since [lib.pairs] p4
Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.
(But, as I said, SFINAE on ctors cannot be implemented in C ++ 03.)
In C ++ 11 and 14, this also fails, but for a different reason. Here, the pair designers are limited to SFINAE. But the restriction requires implicit convertibility ( is_convertible ), whereas a program has UB if the target pair of types cannot be built from sources ( is_constructible ). I wrote a little more about this issue in another SO answer . Interestingly, the proposed solution N4387 to the question mentioned in this other question says:
It should be noted here that for the general case,
std::is_constructible<T, U>::valuerequirement for an implicit constructor that is limited tostd::is_convertible<U, T>::valuenot redundant, since types that can be copy-initialize, but not directly initialize
This is exactly the case that we encounter here: A map can be initialized with a copy from generic_list , as this makes the explicit constructor unviable. But map cannot be directly initialized from generic_list , as this makes the conversion ambiguous.
As far as I can see, N4387 does not solve the problem in the OP. On the other hand, with uniform initialization, we have an alternative to map_list_of . And we can SFINAE-restrict conversion operators with C ++ 11.
One solution is to eliminate the explicit constructor by resolving implicit conversions:
template<typename T> T implicit_cast(T t) { return t; } implicit_cast<InnerMap>( map_list_of(1, 'a')(2, 'b') ) But there is a more direct way: just use the convert_to_container member convert_to_container for the generic_list converter base class (also the class template):
map_list_of(1, 'a')(2, 'b').convert_to_container<InnerMap>()