Почему std :: pair вызывает явные конструкторы в присваивании - PullRequest
4 голосов
/ 20 июля 2011

Рассмотрим следующий код:

#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 работают так же?

Ответы [ 4 ]

5 голосов
/ 20 июля 2011

Я не уверен, что понимаю вопрос, но в основном вы спрашиваете, почему два не связанных std::pair могут быть неявно конвертируемыми, даже если инстанцирующие типы не являются неявно конвертируемыми.Именно поэтому неявно конвертируемое свойство экземплярирующих типов не распространяется на пару.

Стандарт не предоставляет операторов явного присваивания для шаблона std::pair, что означает, что онбудет использовать неявно сгенерированный оператор присваивания.Чтобы иметь возможность назначать пары конвертируемых типов, он опирается на шаблонный конструктор, который допускает неявное преобразование из std::pair<A,B> в std::pair<C,D>, поведение которого определено в §20.2.2 [lib.пары] / 4

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

Эффекты: Инициализирует элементы из соответствующих членов аргумента, выполняя неявные преобразования при необходимости.

Стандарт, кажется, толькотребует, чтобы реализация использовала неявные преобразования, и в этой конкретной реализации преобразование фактически явное, что, по-видимому, противоречит формулировке стандарта.

2 голосов
/ 20 июля 2011

Как часть класса 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>. Это работает для случаев, когда члены first и second могут быть преобразованы в соответствующий член другой пары.

Преобразование каждого члена по отдельности в соответствии со стандартом.

1 голос
/ 20 июля 2011

Это действительно должен быть комментарий, но я предпочитаю, чтобы какое-то место было напечатано.

Итак, допустим, у нас есть два типа:

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

Таким образом, для каждого члена в отдельности мы используем явное преобразование.

0 голосов
/ 20 июля 2011

Быстрый ответ: потому что стандарт гласит:

Ваш следующий вопрос, конечно, будет таким: Почему стандарт так говорит?

Представьте себе эту строку, которая, я думаю, вы согласны, должна работать:

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

Но поскольку 3 и 5 являются целыми числами, мы пытаемся присвоить std::pair<int, int> std::pair<long, long>. Без шаблонного конструктора и шаблонного оператора присваивания произойдет сбой, как доказал ваш MyPair.

Итак, чтобы ответить на ваш вопрос: шаблонный конструктор для удобства. Каждый ожидает, что сможет назначить int на long. Поэтому разумно иметь возможность назначить, например, пара целых в пару длинных.

...