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

C++ 为什么std::pair在赋值中调用显式构造函数,c++,constructor,standards-compliance,explicit,C++,Constructor,Standards Compliance,Explicit,考虑以下代码: #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 <<

考虑以下代码:

#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.
}

是符合C++标准的行为吗?乍一看,它看起来前后矛盾,违反直觉。STL的其他实现是否也以同样的方式工作?

快速回答:因为标准要求它应该这样做

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

想象一下这句话,我认为你同意这句话应该行得通:

std::pair<long, long> x = std::make_pair(3, 5);
std::pair x=std::make_pair(3,5);
但是由于
3
5
都是整数,我们试图将
std::pair
分配给
std::pair
。如果没有模板化构造函数和模板化赋值运算符,它将失败,正如您的
MyPair
所证明的那样


所以要回答您的问题:模板化构造函数是为了方便。每个人都希望能够将int分配给long。因此,能够将一对int分配给一对long是合理的。

作为类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)
    { } 

};
模板
类对
{
公众:
模板
对(常数对和常数对)
:第一(u p.first),
第二(第二)
{ } 
};
不是复制构造函数,而是将构造函数从任何
转换为
。这适用于
第一个
第二个
成员可转换为另一对的相应成员的情况


根据标准分别转换每个成员。

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

该标准没有为
std::pair
模板提供显式赋值运算符,这意味着它将使用隐式生成的赋值运算符。为了能够分配可转换类型对,它依赖于一个模板构造函数,该构造函数允许从
std::pair
隐式转换到
std::pair
,其行为在§20.2.2[lib.pairs]/4中定义

template<class U, class V> pair(const pair<U, V> &p);
模板对(const-pair&p);
效果:从参数的相应成员初始化成员,执行隐式con- 根据需要提供不同的版本


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

这应该是一个注释,但我更希望有一些空间将其打印出来

因此,假设我们有两种类型:

typedef std::pair<A,B> pairAB;
typedef std::pair<S,T> pairST;
由于
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赋值运算符。这才是相关的。std::pair赋值运算符没有定义,或者我是瞎子。@konrad rudolph-我没想到在我赋值时会调用显式构造函数。@Steve:哦,对不起,我误解了对
显式
的强调——我正在删除我的注释!我不认为标准要求这样做。看看我的答案。我唯一能引用的是20.2.2,它要求
std::pair
隐式转换为
std::pair
,根据需要执行隐式转换。该实现实际执行的是显式转换。但是
int
隐式转换为
long
Der2
可显式转换为
Der1
,但不能隐式转换为
。我认为如果
X
不能隐式转换为
Y
,那么
pair
就不应该隐式转换为
pair
。这个实现并没有实现这一点(我不能马上想到它会是什么样子)。
Der2
可以隐式转换为
Base
。还有一个
Der1
构造函数接受
Base
。转换类型时允许一个隐式转换和一个用户定义的转换,因此允许从
Der2
转换到
Der1
。由于存在显式初始化(如果没有?),显式
不禁止此转换。因此,如果不排除显式转换,20.2.2中的“隐式”一词意味着什么?这是否意味着从
U
T1
的转换可以是显式的,但是根据需要执行额外的隐式转换?单独转换每个成员是根据标准的:是的,但是标准将隐式添加到该语句中,在这种情况下,实现是进行显式转换,这就是问题本身的原因。@David你将如何编写这个构造函数?@David-好的,我可能把重点放在这部分而不是赋值或复制构造函数上,而忽略了问题的
显式
部分。C++0x说,如果成员不是隐式可转换的,则不应考虑此构造函数,但C++98标准没有这样说,并且没有提供任何工具来决定它。不过,您实际如何实现这一点?将
\u p.放在初始值设定项列表中的第一个
,调用显式构造函数。但如果不这样做,
this->first
需要是默认可构造的,这也不符合要求。是否小于鳕鱼
x = pairAB(w);  // this happens when we say "x = w;"
pairAB(const pairST & other) : first(other.first), second(other.second) { }