看得到想不到的坑:std::make_pair与std::pair<T1, T2>

今天捣鼓Rocstar,编译的时候碰上了一个C++11的坑,属于那种不告诉你的话,给你代码你都看不出来异常的坑。出问题的那段代码长这样:

int node_id1 = fne[j],node_id2 = fne[(j+1)%nj];
if(node_id1>node_id2)
    std::swap(node_id1, node_id2);
e2f_it = e2f.find(std::make_pair<int,int>(node_id1,node_id2));
if(e2f_it != e2f.end()){
    afs.insert(std::make_pair<int,int>(e2f_it->second,i));
    e2f.erase(e2f_it);
}
else{
    e2f.insert(std::make_pair<std::pair<int,int>,int>(
            std::make_pair<int,int>(node_id1,node_id2)
            ,i));
}

上下文不怎么明确,但猜一猜也知道是怎么回事。看起来跟平时用的std::pair没啥区别吧?然而它就是报错了: Cannot convert parameter 1 from 'int' to 'int &&'

检查了半天,确定node_id1node_id2兄弟俩确实是int,这怎么可能错?实在受不了的我还去看了这一段的STL源码:

#if __cplusplus >= 201103L
  // NB: DR 706.
  template<typename _T1, typename _T2>
    constexpr pair<typename __decay_and_strip<_T1>::__type,
                   typename __decay_and_strip<_T2>::__type>
    make_pair(_T1&& __x, _T2&& __y)
    {
      typedef typename __decay_and_strip<_T1>::__type __ds_type1;
      typedef typename __decay_and_strip<_T2>::__type __ds_type2;
      typedef pair<__ds_type1, __ds_type2> 	      __pair_type;
      return __pair_type(std::forward<_T1>(__x), std::forward<_T2>(__y));
    }
#else
  template<typename _T1, typename _T2>
    inline pair<_T1, _T2>
    make_pair(_T1 __x, _T2 __y)
    { return pair<_T1, _T2>(__x, __y); }
#endif

显然,在201103L之后,标准有过一次变更。Rocstar估计就是那种万年gcc-4.8的工程,所以估计用旧版本编译器就过去了。但新版究竟是怎么错的?看上面的新版代码也不知所以然。没办法,放下面子搜错误吧,还真在这里找到了,问题出在新版make_pair__decay_and_strip<_T1>这部分。

具体来说,就是C++11之后模板推导形式不一样了,合法的用法只有两种:

num_text.push_back(std::make_pair(num, text));               // deduced type
num_text.push_back(std::pair<int, std::string>(num, text));  // specific type

作为一个从C++11时代才开始深入学习的人来说,只有看到这里明明白说出来,才能想起来平时写std::make_pair是不带尖括号的。 改起来就容易了,把原文中make_pair<int,int>一类语句中的尖括号全删掉就好了。

  • 最后更改: 2022/01/04 12:27