Преобразование конструктора с несколькими аргументами
В С++ 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}
?
Ответы
Ответ 1
Вы можете быть особенно удивлены тем, что:
x = {1, 2, 3}; // ok
x == {1, 2, 3}; // error
operator==(x, {1, 2, 3}); // ok
Это связано с тем, что есть только определенные места, где бит-init-список (в основном, разделенный запятой список вещей между {}s
) разрешен для входа на язык. Он может идти по правой стороне =
потому что правила говорят, что это возможно. Он может использоваться в качестве аргумента в выражении вызова функции, поскольку правила говорят, что это возможно. Но он не может использоваться по обе стороны операторов сравнения, потому что правила этого не позволяют.
Я не знаю, есть ли основополагающая причина этого за пределами, возможно, для этого не существует большой потребности.
Ответ 2
Мне интересно, почему преобразование конструктора не конвертирует список {1,2,3}
в объект Date
?
Потому что это не "конвертирующий конструктор". Это просто "конструктор".
Инициализация списка (то, что происходит, когда вы используете бит-init-list) используется для инициализации объекта из списка значений, как можно было бы ожидать от имени. x = {1, 2, 3};
не инициализирует объект. x
- объект, который уже был инициализирован.
Таким образом, скопированные-init-списки не могут быть непосредственно применены к существующему объекту; они могут применяться только к объекту, который инициализируется. То, что вы хотите сделать, это использовать список для инициализации Date
а затем скопировать эту Date
в существующий объект x
. Это записано x = Date{1, 2, 3};
,
"Преобразующий конструктор" - это конструктор, который выполняет неявные преобразования. Неявное преобразование преобразуется из объекта одного типа в объект другого типа. Инициализация списка не является и никогда не выполнялась. Date x = {1, 2, 3};
не преобразовывает список в Date
; он инициализирует Date
со списком, используя правила инициализации списка копий.
Ответ 3
Чтобы завершить настройку Barry, я составил список всех операторов или выражений, в которых может появиться brace-init-list
команд:
- вызов функции:
func({/*...*/},arg2)
- subscriptip:
obj[{/*...*/}]
; - явное преобразование типа:
type{/*...*/}
- новое выражение:
new type{/*...*/}
- назначение и составные назначения:
a = {/*...*/}; b += {/*...*/};...
a = {/*...*/}; b += {/*...*/};...
- в условии условного оператора
while (atype i={/*.../*})
- for-range-initializer
for(auto it:{/*...*/})
- return return:
return {/*.../*}
(если не выведен тип возвращаемого значения) - Инициализатор:
atype a{/*...*}; atype b={/*.../*};
atype a{/*...*}; atype b={/*.../*};
или включая инициализатор члена: a_memb{/*.../*}
- аргумент по умолчанию
void f(atype a={/*.../*})