Конвертирующий конструктор с несколькими аргументами - PullRequest
0 голосов
/ 28 июня 2018

В C ++ 11 конструктор без ключевого слова explicit может использоваться для неявного преобразования списка аргументов в его класс. Например:

class Date{
private:
  int d, m, y;
public:
  Date(int _d, int _m=0, int _y=0) : m(_m), d(_d), y(_y) {}
  friend bool operator==(const Date &x, const Date &y) {return  x.d==y.d;}
};

int main()
{
  Date x = {1,2,3}; // no error; using converting constructor
  x == 1; // no error; converting constructor turns int into Date object
  x == {1,2,3}; // error
}

Для x == {1,2,3} я получил следующую ошибку:

explicit.cc:16:10: error: expected primary-expression before ‘{’ token
       x=={1,2,3};
          ^

Мне интересно, почему преобразование конструктора не преобразует список {1,2,3} в Date объект? Тем более что x == 1 не приводит к ошибке, почему x == {1,2,3}?

Ответы [ 3 ]

0 голосов
/ 28 июня 2018

Мне интересно, почему конвертирующий конструктор не конвертирует список {1,2,3} в Date объект?

Потому что это не "конструктор преобразования". Это просто «конструктор».

Инициализация списка (то, что происходит при использовании braced-init-list) используется для инициализации объекта из списка значений, как можно было бы ожидать от имени. x = {1, 2, 3}; не инициализирует объект. x - это объект, который уже был инициализирован.

Таким образом, braced-init-lists не могут быть напрямую применены к существующему объекту; они могут быть применены только к объекту, который инициализируется. Что вы хотите сделать, это использовать список для инициализации Date, а затем скопировать этот Date в существующий объект x. Это пишется x = Date{1, 2, 3};.

«Конвертирующий конструктор» - это конструктор, который выполняет неявные преобразования. Неявное преобразование преобразует объект одного типа в объект другого типа. Инициализация списка не является и никогда не была операцией преобразование . Date x = {1, 2, 3}; не преобразует список в Date; Date инициализируется списком, используя правила copy-list-initialization.

0 голосов
/ 28 июня 2018

Чтобы завершить ответ Барри, я составил список всех утверждений или выражений, где может появляться brace-init-list:

  • вызов функции: func({/*...*/},arg2)
  • подписка: obj[{/*...*/}];
  • явное преобразование типов: type{/*...*/}
  • новое выражение: new type{/*...*/}
  • назначение и составные задания: a = {/*...*/}; b += {/*...*/};...
  • в условии условного высказывания while (atype i={/*.../*})
  • для инициализации диапазона for(auto it:{/*...*/})
  • оператор возврата: return {/*.../*} (нет, если тип возврата выводится)
  • инициализатор: atype a{/*...*}; atype b={/*.../*}; или включая инициализатор элемента: a_memb{/*.../*}
  • аргумент по умолчанию void f(atype a={/*.../*})
0 голосов
/ 28 июня 2018

Вы можете быть особенно удивлены, что:

x = {1, 2, 3};            // ok
x == {1, 2, 3};           // error
operator==(x, {1, 2, 3}); // ok

Это потому, что есть только определенные места, где braced-init-list (в основном, разделенный запятыми список вещей между {}s) разрешено использовать в языке. Он может идти по правой стороне =, потому что правила говорят, что это возможно. Его можно использовать в качестве аргумента в выражении вызова функции, потому что правила говорят, что это возможно. Но его нельзя использовать ни с одной из сторон операторов сравнения, потому что правила этого не допускают.

Я не знаю, есть ли фундаментальная причина, лежащая в основе этого, и, вероятно, в этом нет особой необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...