【问题标题】:Issue with key of std::mapstd::map 的键问题
【发布时间】:2012-08-31 11:10:55
【问题描述】:

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

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

【问题讨论】:

  • 您的代码无法编译,因为倒数第三行包含“tes”而不是“test”。你能发布你正在编译的实际代码吗?
  • 您可以添加必要的标题并使其成为可编译的示例吗?我无法编译我的版本,所以我想确保我们正在尝试同样的事情。另外,您使用什么编译器和增强版本?

标签: c++ stl stdmap boost-tuples


【解决方案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);
}

带有libc++ 的Clang 4.0 和另一个接受这个,Comeau Online 也接受它。 GCC 4.7.1 报错。

它不能编译,根据:

20.3.2/12

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

备注:除非 const U& 可隐式转换为 first_type 且 const V& 可隐式转换为 second_type,否则此构造函数不应参与重载决议。

20.3.2/23

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

要求:is_assignable&lt;first_type&amp;, const U&amp;&gt;::valuetrueis_assignable&lt;second_type&amp;, const V&amp;&gt;::valuetrue

【讨论】:

  • 您仍然不需要 operator= 引用,因为它从未使用过。 :)
  • 关键条件是隐式可转换的。有一个explicit 转换std::vector&lt;T&gt;::vector(size_t, T = T())
  • 这是我在回复中遗漏的措辞。 (并且由于某种原因,虽然它现在说你在一小时前回答了这个问题,但我在 5 分钟前看不到你的帖子。)然而,这是来自 C++11;在C++03下,他的代码需要编译。
  • @sam 可能是这样,因为根据 JamesKanze 的说法,它在 C++03 中是合法的。但是你不应该再使用它了,因为它现在在 C++11 中是非法的,并且很可能很快会在 Boost 中得到修复。
【解决方案2】:

问题在于隐式转换。不是从intstd::vector&lt;int&gt;;那是行不通的,因为涉及的构造函数 声明了explicit,因此不能用于隐式 转换。隐式转换是从std::pair&lt;int, int&gt;std::pair&lt;int, std::vector&lt;int&gt; &gt;。这使用派生的构造函数 来自模板: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&lt;int&gt;(这是第二个 论据应该是)。但是...在 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 的构造函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-07
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-25
    • 1970-01-01
    相关资源
    最近更新 更多