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

发布于 2024-11-25 11:31:55 字数 1253 浏览 1 评论 0原文

考虑以下代码:

#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 的其他实现是否也以同样的方式工作?

Consider the following code:

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

Tested under GCC 4.5.2

The reason lies in std::pair sources:

  /** 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) { }

Is that behaviour compliant with the C++ standard? For a first sight it looks inconsistent and counterintuitive. Do the other implementations of STL work the same way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

╰つ倒转 2024-12-02 11:31:55

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

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

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

效果:从参数的相应成员初始化成员,执行隐式 con-
根据需要版本。

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

I am not sure that I understand the question, but basically you are asking why two unrelated std::pair can be implicitly convertible even if the instantiating types are not implicitly convertible. That is, why the implicitly convertible property of the instantiating types does not propagate to the pair.

The standard does not provide explicit assignment operators for the std::pair template, which means that it will use the implicitly generated assignment operator. To be able to assign pairs of convertible types, it relies on a templated constructor that allows an implicit conversion from std::pair<A,B> to std::pair<C,D>, the behavior of which is defined in §20.2.2 [lib.pairs]/4

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

Effects: Initializes members from the corresponding members of the argument, performing implicit con-
versions as needed.

The standard seems to only require the implementation to use implicit conversions, and in this particular implementation the conversion is actually explicit, which seems to contradict the wording of the standard.

半﹌身腐败 2024-12-02 11:31:55

作为类 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 的转换构造函数。这适用于 firstsecond 成员可转换为另一对的相应成员的情况。

每个成员单独转换是根据标准进行的。

As part of class std::pair the constructor

template<class T1, T2>
class pair
{
public:

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

};

is not a copy constructor, but a converting constructor from any pair<_U1, _U2> to pair<T1, T2>. This works for cases where the first and second members are convertible to the corresponding member of the other pair.

Converting each member separately is according to the standard.

水染的天色ゝ 2024-12-02 11:31:55

这确实应该是一条评论,但我更喜欢有一些空间来写出来。

所以,假设我们有两种类型:

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

现在我想将一种类型分配给另一种类型:

pairAB x;
pairST w;

x = w; // how?

由于 std::pair 没有显式赋值运算符,因此我们只能使用默认赋值 对AB &运算符=(常量pairAB&amp;)。因此,我们调用隐式转换构造函数,这相当于:

x = pairAB(w);  // this happens when we say "x = w;"

但是,已经指出,此转换构造函数调用显式成员构造函数:

pairAB(const pairST & other) : first(other.first), second(other.second) { }

因此,对于每个成员,我们分别一定要使用显式转换。

This should really be a comment, but I prefer some room to type this out.

So, lets say we have two types:

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

Now I want to assign one to the other:

pairAB x;
pairST w;

x = w; // how?

Since std::pair doesn't have an explicit assigment operator, we can only use the default assignment pairAB & operator=(const pairAB &). Thus we invoke the implicit conversion constructor, which is equivalent to:

x = pairAB(w);  // this happens when we say "x = w;"

However, has has been pointed out, this conversion constructor calls the explicit member constructors:

pairAB(const pairST & other) : first(other.first), second(other.second) { }

Thus for each member individually we do use explicit conversion.

宣告ˉ结束 2024-12-02 11:31:55

快速回答:因为标准说应该这样做。

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

想象一下这一行,我认为你同意它应该起作用:

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

但是由于 35 是整数,我们试图分配一个 std::pairstd::pair。如果没有模板化构造函数和模板化赋值运算符,它将失败,正如您的 MyPair 所证明的那样。

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

Quick answer: Because the standard says it should.

Your next question, of course, will be: Why does the standard say so?

Imagine this line, which I think you agree should work:

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

But as 3 and 5 are ints, we are trying to assign a std::pair<int, int> to a std::pair<long, long>. Without a templated constructor and a templated assignment operator, it would fail, as your MyPair proved.

So to answer your question: the templated constructor is for convenience. Everyone expects to be able to assign an int to a long. So it is reasonable to be able to assign e.g. a pair of ints to a pair of longs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文