2015-05-01 2 views
8

Considerate questo:Sto cercando di annidare "map_list_of" di boost in C++ 03, ma apparentemente la costruzione è ambigua?

#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"; 
} 

ho voluto il risultato per essere un programma che, una volta eseguito, uscite d.

Invece, 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. 

(risultati simili sotto GCC)

Come posso risolvere questo?

Viene visualizzato un errore simile anche se utilizzo std::map<int, char>(map_list_of(...)) anziché map_list_of(...) per quelle mappe interne.

+0

Tuttavia, funziona con un "cast implicito" invece di un cast di stile funzione. 'template T implicit_cast (T t) {return t; } 'Ma questo tipo di sconfigge lo scopo di' map_list_of'. (I costruttori di allocatore e comparatore di 'map' sono' espliciti'.) – dyp

+0

@dyp: Huh, [nice] (http://coliru.stacked-crooked.com/a/dbcb1865c54d5f7b)! Questa è la risposta. Non sconfigge affatto lo scopo, perché ho ancora la mia mappa 'const' con inizializzatore" in linea ". –

+0

@dyp Perché il cast implicito funziona? – 0x499602D2

risposta

8

C++ 03 definisce due costruttori per map che possono essere chiamati con un argomento [lib.map] p2:

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

spinta di map_list_of crea un oggetto di un modello di un'istanza generic_list di classe, dalla recente 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) 

Qualora il generic_list modello primario contiene il seguente operatore di conversione:

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

Entrambi map costruttori sono vitali, come questo operatore permette la conversione sia map e Compare. Per quanto ne so, non è possibile SFINAE-vincolare un operatore di conversione in C++ 03.


Il map è costruito esplicitamente quando si inserisce un nuovo nodo nella esterno cartina . Una coppia di iteratori viene utilizzata per iterare all'interno dello generic_list interno per costruire l'esterno map. Dereferenziare questo iteratore produce un std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> >. Il tipo di nodo (valore) della mappa esterna è std::pair<int const, std::map<int, char> >.

Pertanto, il compilatore tenta di costruire quest'ultimo tipo dal precedente. In C++ 03, questo costruttore pair non ha vincoli SFINAE, poiché ciò non è possibile in C++ 03. [Lib.pairs] p1

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

libstdC++ implementa questa come segue:

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

io non sono del tutto sicuro se questo è compatibile, dal momento che [lib.accoppiamenti] p4

Effetti: inizializza membri dai corrispondenti componenti del ragionamento, eseguendo conversioni implicite come necessario.

(Ma, come detto, sulla SFINAE ctors non può essere implementato in C++ 03.)

In C++ 11 e 14, anche questo non funziona, ma per una ragione diversa. Qui, i costruttori di coppie sono vincolati a SFINAE. Ma il vincolo richiede la convertibilità implicita (is_convertible), mentre il programma ha UB se la coppia di tipi di destinazione non può essere costruita da (is_constructible). Ho scritto un po 'di più su questo problema in another SO answer. È interessante notare che una proposta di soluzione N4387 al problema menzionato in tale altra domanda dice:

Si deve notare qui, che per il caso generale della std::is_constructible<T, U>::value requisito per il costruttore non esplicito, che è costretto su std::is_convertible<U, T>::value non è ridondante, perché è possibile creare tipi che possono essere copia-inizializzato ma non diretta-inizializzato

Questo è esattamente il caso ci imbattiamo in qui: Un map può essere copia-inizializzato da a generic_list, poiché ciò rende il costruttore explicit non vitale. Ma un map non può essere inizializzato direttamente da generic_list, poiché ciò rende la conversione ambigua.

Per quanto posso vedere, N4387 non risolve il problema nell'OP. D'altra parte, con l'inizializzazione uniforme, abbiamo un'alternativa a map_list_of. E possiamo utilizzare gli operatori di conversione di vincoli SFINAE dal C++ 11.


Una soluzione è quella di eliminare il costruttore explicit consentendo solo conversioni implicite:

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

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

Ma c'è un modo più diretto: utilizzare semplicemente la funzione convert_to_container membro della classe base generic_list s' converter (anche un modello di classe):

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

Non sto passando attraverso la risoluzione di sovraccarico qui e assumerò solo che l'interpretazione di clang e gcc sia corretta e che la chiamata sia davvero ambigua. – dyp

+0

Glorioso. Grazie! –