我试图在 C++03 中嵌套 boost 的 "map_list_of",但显然构造不明确?
I'm trying to nest boost's "map_list_of" in C++03, but apparently construction is ambiguous?
考虑一下:
#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";
}
我希望结果是一个程序,在执行时输出 d
。
相反,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.
(在 GCC 下结果相似)
我该如何解决这个问题?
即使我对那些内部映射使用 std::map<int, char>(map_list_of(...))
而不是 map_list_of(...)
,我也会遇到类似的错误。
C++03 为 map
定义了两个可以用一个参数调用的构造函数 [lib.map]p2:
explicit map(const Compare& comp = Compare(),
const Allocator& = Allocator());
// [...]
map(const map<Key,T,Compare,Allocator>& x);
boost 的 map_list_of
创建一个 generic_list
class 模板实例化的对象,来自最近的 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 )
其中主要 generic_list
模板包含以下转换运算符:
template< class Container >
operator Container() const
{
return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>();
}
两个 map
构造函数都是可行的,因为此运算符允许转换为 map
和 Compare
。据我所知,您不能在 C++03 中对转换运算符进行 SFINAE 约束。
在外部映射中插入新节点时显式构造map
。一对迭代器用于迭代内部 generic_list
以构造外部 map
。取消引用此迭代器会产生 std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> >
。外层映射的节点(值)类型为std::pair<int const, std::map<int, char> >
.
因此,编译器试图从前者构造后者。在 C++03 中,此 pair
构造函数不受 SFINAE 约束,因为这在 C++03 中是不可能的。 [lib.pairs]p1
template<class U, class V> pair(const pair<U, V> &p);
libstdc++ 实现如下:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first), second(__p.second) { }
我不确定这是否合规,因为 [lib.pairs]p4
Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.
(但是,正如我所说,在 ctors 上的 SFINAE 不能在 C++03 中实现。)
在 C++11 和 14 中,这也失败了,但原因不同。这里,对构造子是 SFINAE 约束的。但是约束需要隐式转换 (is_convertible
),而如果目标类型对不能从来源(is_constructible
)。我在 another SO answer. Interestingly, a proposed solution N4387 中针对另一个问题中提到的问题写了更多关于这个问题的内容:
It should be noted here, that for the general case the
std::is_constructible<T, U>::value
requirement for the non-explicit
constructor which is constrained on std::is_convertible<U, T>::value
is not redundant, because it is possible to create types that can be
copy-initialized but not direct-initialized
这正是我们 运行 这里的情况:A map
可以从 generic_list
复制初始化,因为这使得 explicit
构造函数非可行的。但是 map
不能直接从 generic_list
初始化,因为这会使转换不明确。
据我所知,N4387并没有解决OP中的问题。另一方面,通过统一初始化,我们有 map_list_of
的替代方案。我们可以从 C++11 开始对转换运算符进行 SFINAE 约束。
一种解决方案是通过仅允许隐式转换来消除 explicit
构造函数:
template<typename T> T implicit_cast(T t) { return t; }
implicit_cast<InnerMap>( map_list_of(1, 'a')(2, 'b') )
但是有一个更直接的方法:简单地使用 generic_list
的基础 class converter
的 convert_to_container
成员函数(也是一个 class 模板):
map_list_of(1, 'a')(2, 'b').convert_to_container<InnerMap>()
考虑一下:
#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";
}
我希望结果是一个程序,在执行时输出 d
。
相反,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.
(在 GCC 下结果相似)
我该如何解决这个问题?
即使我对那些内部映射使用 std::map<int, char>(map_list_of(...))
而不是 map_list_of(...)
,我也会遇到类似的错误。
C++03 为 map
定义了两个可以用一个参数调用的构造函数 [lib.map]p2:
explicit map(const Compare& comp = Compare(), const Allocator& = Allocator()); // [...] map(const map<Key,T,Compare,Allocator>& x);
boost 的 map_list_of
创建一个 generic_list
class 模板实例化的对象,来自最近的 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 )
其中主要 generic_list
模板包含以下转换运算符:
template< class Container > operator Container() const { return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>(); }
两个 map
构造函数都是可行的,因为此运算符允许转换为 map
和 Compare
。据我所知,您不能在 C++03 中对转换运算符进行 SFINAE 约束。
在外部映射中插入新节点时显式构造map
。一对迭代器用于迭代内部 generic_list
以构造外部 map
。取消引用此迭代器会产生 std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> >
。外层映射的节点(值)类型为std::pair<int const, std::map<int, char> >
.
因此,编译器试图从前者构造后者。在 C++03 中,此 pair
构造函数不受 SFINAE 约束,因为这在 C++03 中是不可能的。 [lib.pairs]p1
template<class U, class V> pair(const pair<U, V> &p);
libstdc++ 实现如下:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first), second(__p.second) { }
我不确定这是否合规,因为 [lib.pairs]p4
Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.
(但是,正如我所说,在 ctors 上的 SFINAE 不能在 C++03 中实现。)
在 C++11 和 14 中,这也失败了,但原因不同。这里,对构造子是 SFINAE 约束的。但是约束需要隐式转换 (is_convertible
),而如果目标类型对不能从来源(is_constructible
)。我在 another SO answer. Interestingly, a proposed solution N4387 中针对另一个问题中提到的问题写了更多关于这个问题的内容:
It should be noted here, that for the general case the
std::is_constructible<T, U>::value
requirement for the non-explicit constructor which is constrained onstd::is_convertible<U, T>::value
is not redundant, because it is possible to create types that can be copy-initialized but not direct-initialized
这正是我们 运行 这里的情况:A map
可以从 generic_list
复制初始化,因为这使得 explicit
构造函数非可行的。但是 map
不能直接从 generic_list
初始化,因为这会使转换不明确。
据我所知,N4387并没有解决OP中的问题。另一方面,通过统一初始化,我们有 map_list_of
的替代方案。我们可以从 C++11 开始对转换运算符进行 SFINAE 约束。
一种解决方案是通过仅允许隐式转换来消除 explicit
构造函数:
template<typename T> T implicit_cast(T t) { return t; }
implicit_cast<InnerMap>( map_list_of(1, 'a')(2, 'b') )
但是有一个更直接的方法:简单地使用 generic_list
的基础 class converter
的 convert_to_container
成员函数(也是一个 class 模板):
map_list_of(1, 'a')(2, 'b').convert_to_container<InnerMap>()