2015-12-31 5 views
7

Я просмотрел множество вопросов, связанных с преобразованием, но, похоже, никто из них не обсуждал о явном ключевом слове таким образом. Вот код:(с явным) Приоритет с преобразованием оператора и конструктора

struct B; 
struct A{ 
    /*explicit*/ A(const B&){ cout << 1; } // *1 
}; 
struct B{ 
    /*explicit*/ operator A()const{ cout << 2; } // *2 
}; 

void foo(const A &){} 

int main(void){ 
    B b; 
    foo(/*static_cast<A>*/ (b)); // *3 
} 

Результат: (Y: раскомментировал, N: комментировал, X: либо)

# | *1 | *2 | *3 |output| 
1 | N | N | N |error | 
2 | N | N | Y | 1 | 
3 | N | Y | N | 1 | 
4 | N | Y | Y | 1 | 
5 | Y | N | N | 2 | 
6 | Y | N | Y | 1 | 
7 | Y | Y | N |error | 
8 | Y | Y | Y | 1 | 

1, 7 ошибки, что является нормальным (неоднозначны и не авто преобразования.)
2 кажется, что конструктор имеет более высокий приоритет, но почему?
3, 5 легко понять.
4 странно, поскольку он не вызывает явного. Зачем?
6 может быть вызвано «явным» или конструктором с более высоким приоритетом. Какая причина? 8 кажется, что конструктор имеет более высокий приоритет, но почему?

Может кто-нибудь предложить некоторые объяснения? Благодаря!

+0

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

+0

@BoPersson, я это знаю. Мне просто интересно, почему компилятор работает так. Я знаю, это не практический вопрос. Спасибо – Asu

ответ

2

Очень хороший вопрос.

Прежде всего, вещь explicit не означает «это имеет приоритет, если требуется явное преобразование». Это означает, что «эта вещь может быть вызвана явно». Поэтому он создает некоторые ситуации, в которых он не может быть вызван, а не принуждает его вызываться в других ситуациях.

Другое дело, что static_cast является direct initialization, передавая аргумент функции copy initialization. Помимо прочего, инициализация копирования никогда не использует явные конструкторы. Другое дело отметить, что для прямой инициализации требуется использование конструкторов для классов (явно или нет). Хотя это не означает, что преобразование не может выполнить прямую инициализацию: его можно использовать для преобразования аргумента конструктора, и если конструктор является копией, сгенерированной компилятором, то он будет посмотреть как выполненная функция преобразования прямая инициализация (тогда как на самом деле это выполнялось конструктором копирования). Попробуйте объявить конструктор копии без его определения (метод отключения), и вы увидите, что функция преобразования больше не работает в контексте прямой инициализации: она будет компилироваться, но приведет к ошибке связывания.

Имея это в виду:

  1. Очевидное.
  2. Для прямой инициализации требуется конструктор, поэтому он вызывается.
  3. Очевидно.
  4. То же, что и 2, действительно. Объявление функции преобразования explicit только предотвращает ее неявное выключение, она не заставляет ее использовать в явных контекстах.
  5. Очевидно.
  6. Опять же, для прямой инициализации требуется конструктор, и он позволяет использовать явные.
  7. Очевидно.
  8. Другой прямой случай инициализации.
+0

Я изменил функцию foo на void foo (A) {} и добавить A (const A &) = удалить; в структуре A. Выполните его в случае № 5 и да, это ошибка. Благодаря! – Asu

+0

Кроме того, похоже, что так называемый «приоритет» на самом деле является процессом перегрузки. Конструктор преобразования является точным совпадением, в то время как преобразование оператора фактически использует конструктор копирования с преобразованием параметров, который не является точным совпадением. Надеюсь, я не понял это неправильно. – Asu

+0

@Asu, если быть более точным, это называется перегрузкой _resolution_ process, но да, это общая идея. И я не думаю, что вам нужно изменить подпись 'foo'. Ссылка или нет, так как тип фактического параметра отличается от типа формального параметра, компилятору все равно нужно создать копию в любом случае, чтобы передать ее самостоятельно или ссылку на нее. –