3

考虑以下代码。由整数和整数向量组成的元组被定义为映射的键。但是,令我惊讶的是,在插入或查找由整数和整数作为键的元组时,编译器没有抛出任何错误。这怎么可能,因为元组的第二个元素应该是 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";  
4

2 回答 2

1

似乎是 Boost 和许多 C++ 标准库实现中的一个错误。这个问题由pair和共同解决tuple。演示它的最简单代码是:

#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.0libc++和另一个接受这个,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<first_type&, const U&>::valuetrueis_assignable<second_type&, const V&>::valuetrue

于 2012-08-31T12:22:44.963 回答
1

问题是隐式转换。不是从intstd::vector<int>; 那是行不通的,因为那里涉及的构造函数是声明explicit的,因此不能用于隐式转换。隐式转换是 fromstd::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不是 a 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.

于 2012-08-31T14:00:18.447 回答