为什么std::pair在赋值中调用显式构造函数

Why std::pair calls explicit constructors in assignment

本文关键字:调用 构造函数 赋值 std pair 为什么      更新时间:2023-10-16

考虑以下代码:

#include<iostream>
#include<utility>

struct Base
{
    int baseint;
};
struct Der1 : Base
{
    int der1int;
    Der1() : der1int(1) {}
    explicit Der1(const Base& a) : Base(a), der1int(1)
    {
        std::cerr << "cc1" << std::endl;
    }
};
struct Der2 : Base
{
    int der2int;
    Der2() : der2int(2) {}
    explicit Der2(const Base& a) : Base(a), der2int(2)
    {
        std::cerr << "cc2" << std::endl;
    }
};

template <typename T, typename U>
struct MyPair
{
    T first;
    U second;
};
int main()
{
    Der1 d1;
    Der2 d2;
    std::pair<Der1, int> p1;
    std::pair<Der2, int> p2;
    p1 = p2; // This compiles successfully
    MyPair<Der1, int> mp1;
    MyPair<Der2, int> mp2;
    mp1 = mp2; // This will raise compiler error, as expected.
}

在GCC 4.5.2下测试

原因在于std::pair来源:

  /** There is also a templated copy ctor for the @c pair class itself.  */
  template<class _U1, class _U2>
    pair(const pair<_U1, _U2>& __p)
    : first(__p.first),
      second(__p.second) { }

该行为是否符合c++标准?乍一看,它似乎不一致,违反直觉。STL的其他实现以同样的方式工作吗?

我不确定我理解这个问题,但基本上你是在问为什么两个不相关的std::pair可以隐式转换,即使实例化类型不是隐式转换。这就是为什么实例化类型的隐式可转换属性不会传播到对。

标准没有为std::pair模板提供显式赋值操作符,这意味着它将使用隐式生成的赋值操作符。为了能够赋值可转换类型对,它依赖于一个模板化的构造函数,该构造函数允许从std::pair<A,B>std::pair<C,D>隐式转换,其行为在§20.2.2 [lib.pairs]/4

中定义。
template<class U, class V> pair(const pair<U, V> &p);

效果:从实参的相应成员初始化成员,执行隐式con-

标准似乎只要求实现使用隐式转换,在这个特定的实现中,转换实际上是显式的,这似乎与标准的措辞相矛盾。

std::pair类的构造函数

template<class T1, T2>
class pair
{
public:
    template<class _U1, class _U2>
    pair(const pair<_U1, _U2>& __p)
         : first(__p.first),
           second(__p.second)
    { } 
};

不是复制构造函数,而是将任意pair<_U1, _U2>转换为pair<T1, T2>的构造函数。这适用于firstsecond成员可转换为另一对对应成员的情况。

按标准分别转换各成员

这应该是一个注释,但我希望有一些空间来输入。

那么,假设我们有两种类型:

typedef std::pair<A,B> pairAB;
typedef std::pair<S,T> pairST;

现在我想把一个赋值给另一个:

pairAB x;
pairST w;
x = w; // how?

由于std::pair没有显式赋值操作符,因此只能使用默认赋值pairAB & operator=(const pairAB &)。因此,调用隐式转换构造函数,相当于:

x = pairAB(w);  // this happens when we say "x = w;"
然而,前面已经指出,该转换构造函数调用显式成员构造函数:
pairAB(const pairST & other) : first(other.first), second(other.second) { }

快速回答:因为标准说它应该。

你的下一个问题当然是:为什么标准这么说?

想象这一行,我认为你应该同意它的工作:

std::pair<long, long> x = std::make_pair(3, 5);

但是由于35是整型,我们试图将std::pair<int, int>赋值给std::pair<long, long>。如果没有模板化的构造函数和模板化的赋值操作符,它将失败,正如您的MyPair所证明的那样。

那么回答你的问题:模板化构造函数是为了方便。每个人都希望能够将int赋值给long类型。因此,将一对int型赋值给一对long型是合理的。