问题与std :: map的关键

时间:2012-08-31 11:10:55

标签: c++ stl stdmap boost-tuples

请考虑以下代码。由整数和整数向量组成的元组被定义为映射的关键字。但是,我很惊讶编译器在插入或查找由整数和整数组成的元组作为键时不会抛出任何错误。怎么会这样,因为元组的第二个元素应该是整数的类型?

std::map <boost::tuple<int, vector<int > >, int> test;
std::map <boost::tuple<int, vector<int > >, int>::iterator test_it;

vector <int> t;
t.push_back(4);

test.insert(make_pair(boost::make_tuple(3, t), 4));

test.insert(make_pair(boost::make_tuple(3, 6), 4));

test_it = test.find(boost::make_tuple(3, 7)); 
if(test_it != test.end()) 
throw " test is passed";  

2 个答案:

答案 0 :(得分:1)

似乎是Boost中的一个错误和许多C ++标准库实现。问题由pairtuple共享。用于演示它的最简单的代码是:

#include <vector>
#include <utility>
using namespace std;
int main() {
    //compiles
    pair<int,vector<int>> bug1( pair<int,int>(5,6) );

    //compiles
    pair<int,vector<int>> bug2;
    bug2 = pair<int,int>(5,6);
}

Clang 4.0与libc++,另一个接受此,Comeau Online也接受它。 GCC 4.7.1给出了错误。

根据:

,它不能编译
  

20.3.2 / 12

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

备注:此构造函数不应参与重载决策,除非const U&amp;可隐式转换为first_type和const V&amp;可以隐式转换为second_type。

     

20.3.2 / 23

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

要求:is_assignable<first_type&, const U&>::valuetrueis_assignable<second_type&, const V&>::valuetrue

答案 1 :(得分:1)

问题是隐式转换。它不是来自int std::vector<int>;这不起作用,因为涉及的构造函数 声明explicit,因此不能用于隐式 转换。隐式转换是从std::pair<int, int>到。{ std::pair<int, std::vector<int> >。这使用派生的构造函数 来自模板:template <typename U1, typename U2> std::pair( std::pair<U1, U2> const& ),这不是隐含的。和定义 这个构造函数是:

template <typename T1, typename T2>
template <typename U1, typename U2>
std::pair<T1, T2>::std::pair( std::pair<U1, U2> const& other )
    : first( other.first )
    , second( other.second )
{
}

(这不是标准如何指定它。但是 C ++ 03中的规范不允许其他内容。在C ++ 11中,有很多 额外的行李,以便在可能的情况下搬运物品, 但我认为最后的效果是相同的。)

请注意,在此构造函数中,有一个显式调用 构造函数,而不是隐式转换。所以对于隐含的 将pair转换为工作,这两种类型就足够了 明确可转换。

就个人而言,我怀疑这是最初的意图。我怀疑,在 事实上,围绕std::pair的大多数语言都被冻结了 在explicit添加到语言之前,所以没有问题。 后来,没有人想过重新审视这个问题。在C ++ 11中,重新审视它 会破坏向后兼容性。所以你会有一些意外的 转化

请注意,这不是转发导致的唯一情况 显式转换成隐式。考虑:

std::vector<std::vector<int> > v2D( 5, 10 );

显然,10不是std::vector<int>(这是第二个 论证应该是)。但是......在C ++ 03中,这与构造函数匹配 模板:

template<typename ForwardIterator, typename ForwardIterator>
std::vector( ForwardIterator begin, ForwardIterator end );

标准有一些特殊的语言:

- 构造函数

template <class InputIterator>
X(InputIterator f, InputIterator l, const Allocator& a = Allocator())

具有与以下相同的效果:

X(static_cast<typename X::size_type>(f),
static_cast<typename X::value_type>(l), a)

如果InputIterator是整数类型。

隐式转换变得明确。

(请注意,没有这种特殊语言,

std::vector<int> v(10, 42);

无法编译:上面模板构造函数的实例化, 是完全匹配,优于std::vector<int>( size_t, int )。该委员会认为需要明确的第一个演员 上面的整数,size_t可能会对用户提出太多要求。)

C ++ 11显着改变了这里的措辞,并且:

std::vector<int, std::vector<int>> v2D( 10, 42 );

不再合法。

没有任何这样的改变,我可以看到的,也适用于 std::pair的构造函数。