【问题标题】:I'm trying to nest boost's "map_list_of" in C++03, but apparently construction is ambiguous?我试图在 C++03 中嵌套 boost 的“map_list_of”,但显然构造是模棱两可的?
【发布时间】:2015-05-01 14:00:50
【问题描述】:

考虑一下:

#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&lt;int, char&gt;(map_list_of(...)) 而不是 map_list_of(...) 用于那些内部地图,我也会遇到类似的错误。

【问题讨论】:

  • 它确实可以使用“隐式转换”而不是函数式转换。 template&lt;typename T&gt; T implicit_cast(T t) { return t; } 但这违背了map_list_of 的目的。 (map 的分配器和比较器构造器是explicit。)
  • @dyp:嗯,nice!这就是答案。它根本没有破坏目的,因为我仍然使用“内联”初始化程序获得我的 const 映射。
  • @dyp 为什么隐式转换有效?
  • @0x499602D2 因为它阻止了mapexplicit 构造函数成为候选。看起来只有一个构造函数。
  • @dyp:那会是一个破解的答案

标签: c++ boost c++03


【解决方案1】:

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 类模板实例化的对象,来自最近的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 两个构造函数都是可行的,因为此运算符允许转换为 mapCompare。据我所知,您不能在 C++03 中对转换运算符进行 SFINAE 约束。


map 是在 outer 映射中插入新节点时显式 构造的。一对迭代器用于迭代内部generic_list 以构造外部map。取消引用这个迭代器会产生一个std::pair&lt;int, boost::assign_detail::generic_list&lt;std::pair&lt;int, char&gt; &gt;。外层映射的节点(值)类型为std::pair&lt;int const, std::map&lt;int, char&gt; &gt;

因此,编译器尝试从前者构造后者类型。在 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

效果:从参数的相应成员初始化成员,根据需要执行隐式转换。

(但是,正如我所说,ctors 上的 SFINAE 无法在 C++03 中实现。)

在 C++11 和 14 中,这也会失败,但原因不同。在这里,对构造函数是 SFINAE 约束的。但是约束需要隐式可转换性 (is_convertible),而如果目标类型对不能从源 (is_constructible)构造,则程序具有 UB。我在another SO answer 中写了更多关于这个问题的文章。有趣的是,针对其他问题中提到的问题提出的解决方案N4387 说:

这里应该注意的是,对于一般情况 std::is_constructible&lt;T, U&gt;::value 要求为非显式 受std::is_convertible&lt;U, T&gt;::value约束的构造函数 不是多余的,因为可以创建可以 复制初始化但不直接初始化

这正是我们在这里遇到的情况: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的基类converter(也是类模板)的convert_to_container成员函数:

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

【讨论】:

  • 我不会在这里进行重载解析,只是假设 clang 和 gcc 的解释是正确的,并且调用确实模棱两可。
猜你喜欢
  • 2019-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
  • 2014-10-21
  • 2021-12-19
  • 1970-01-01
相关资源
最近更新 更多