2016-04-28 3 views
2

я увидел следующие слова в стандартном проекте С ++ N4582:Когда пользовательские последовательности преобразований не рассматриваются для выбора жизнеспособных конструкторов в C++?

[over.best.ics/4] Однако, если целью является

(4,1) первый параметр конструктора или

(4.2) неявный параметр объекта из определенной пользователем функции преобразования

и конструктор или определяемые пользователем функции преобразования является кандидатом на

(4.3) 13.3.1.3, когда аргумент является временным во втором шаге класса копирования инициализации или

(4,4) 13.3.1.4, 13.3.1.5, 13.3.1.6 или (во всех случаев),

Определенные пользователем последовательности преобразований не учитываются.

Я смущен о смелой части и не знаю, как ее понять. Я пишу следующую программу:

#include <iostream> 
using namespace std;  
struct A { 
    A(int) {} 
    operator int() {cout << "user-defined conversion" << endl; return 0;} 
    A(A&) {} //prevent default copy 
}; 
int main() 
{ 
    A a = A(0); 
} 

Это хорошо работает в г ++ 5.3.0, и выход «определенный пользователь преобразование», что означает, что определенный пользователь преобразование происходит. Разумеется, это можно интерпретировать как временное A (0) не является следствием копирование-инициализация. Затем я изменить программу:

#include <iostream> 
using namespace std;  
struct A { 
    A(int) {} 
    operator int() {cout << "user-defined conversion" << endl; return 0;} 
    A(A&) {} //prevent default copy 
}; 
A foo() {return A(0);} 
int main() 
{ 
    A a = foo(); 
} 

Теперь значение Foo() является временной копия инициализируется из A (0), но программа все еще работает. Почему это произойдет?

+0

Ваша котировка неверна; вы опустили 4.2 и перенумеровали с 4.3 до 4.2. – ecatmur

+1

Вы понимаете, что означает «второй шаг»? –

+0

@ecatmur Извините за эту неправильную цитату. – xskxzr

ответ

4

Вы можете прочитать: [dcl.init]/17 для стандартного стандартного. «Второй шаг» здесь относится к копированию-инициализации переменной типа класса A от чего-то b неродственного типа. В таком случае, копия инициализация происходит в два этапа:

  • Шаг 1: вы неявно преобразовать b в A. Если вы вызываете конструктор преобразования для этого, он создает временный A.
  • Шаг 2: Затем вы инициализируете переменную A из результата преобразования. (В нормальных классах это обычно отменяется.)

Что это за цитата, так это то, что вы не делаете определяемые пользователем преобразования на этом втором шаге.

Например, с A, A a = 0;. На первом этапе вы делаете A временным от 0. На втором этапе вы пытаетесь инициализировать a с этим временным - без использования пользовательских преобразований. Это не удается, потому что ни конструктор A не является жизнеспособным.

+0

И обоснование того, что на втором этапе не выполняется пользовательские преобразования, заключается в том, что вы не делаете двух пользовательских преобразований в одной последовательности преобразования. – MSalters

+0

@MSalters Итак, почему явным образом запрещаю это, если это не сработает? – Barry

+0

@Barry: Это может определенно работать; просто компилятор нуждается в _some_ limit. В настоящее время компилятор может отказаться, если одного УДК недостаточно. Если вы установите ограничение на 2, ему необходимо проверить все возможные пары. Если вы установите ограничение на 3, компилятор должен проверить все триплеты. Сложность явно экспоненциальна по числу разрешенных УДК в одной последовательности. И имейте в виду, что конкретный UDC может быть шаблонизирован и, следовательно, требует создания экземпляра, поэтому даже этот уровень проверки уже стоит дорого. – MSalters

0

Существует только 2 ctors, один из которых ссылается на существующий объект (без временных) и тот, который принимает int.

Ваш код производит (с дополнительными сообщениями):

int ctor 
user-defined conversion 
int ctor 
user-defined conversion 
int ctor 

Первая конструкция внутри foo.

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

Третий - это потому, что он использует ctor, который принимает int для построения возвращаемого значения.

Четвертый (как второй) из-за того, что возвращаемое значение не может использоваться для построения a, поэтому оно преобразует его в int.

Пятый - это потому, что он использует ctor, который принимает int для сборки a.