2015-05-06 3 views
13

У меня есть кое-что вдоль линий:шаблона приоритета оператора преобразования и константность

#include <iostream> 

class Foo; 

struct Test 
{ 
    template <typename T> 
    operator T() const // <----- This const is what puzzles me 
    { 
     std::cout << "Template conversion" << std::endl; 
     return T{}; 
    } 

    operator Foo*() 
    { 
     std::cout << "Pointer conversion" << std::endl; 
     return nullptr; 
    } 
}; 

int main() 
{ 
    Test t; 

    if (t) 
    { 
     std::cout << "ahoy" << std::endl; 
    } 
    bool b = (bool)t; 
    Foo* f = (Foo*)t; 
} 

Он строит хорошо, но когда я запускаю его, в то время как я ожидаю получить

$> ./a.out 
Template conversion 
Template conversion 
Pointer conversion 

я вместо того, чтобы получить

$> ./a.out 
Pointer conversion 
Pointer conversion 
Pointer conversion 

Если я удаляю const или создаю экземпляр Test const, то все работает так, как ожидалось. Точнее, выбор перегрузки, по-видимому, имеет смысл, когда оба оператора имеют одинаковую квалификацию.

13.3.3.1.2 точка стандарта заставляет меня думать, что я должен получить преобразование идентичности, преобразование в BOOL, используя преобразование шаблона оператора экземпляра с T = bool, хотя, очевидно, скрывается где-то тонкость. Может ли кто-нибудь просветить меня о том, какое правило здесь играет?

+0

оператор Foo * имеет более высокий приоритет, чем оператор шаблона, а Foo * неявно конвертируется в bool, поэтому компилятор выбирает перегрузку Foo *, а не шаблонную. – Creris

+0

Должен ли потенциальный экземпляр, создающий преобразование идентичности, иметь более высокий приоритет? Кажется, что константа - это в основном то, что делает правильное преобразование выбранным – chouquette

+0

, по-видимому, нет, если оно задумано. – Creris

ответ

3

При сравнении последовательностей преобразования перед преобразованием типа результата учитываются преобразования параметров. Параметр неявный объект (указатель this) рассматривается как параметр, а преобразование квалификации (Foo -> Foo const) хуже, чем преобразование идентичности по неявному объекту. Из [over.match.best]:

1 - [...] жизнеспособный функция F1 де определено быть лучше, чем функция другой жизнеспособной функции F2, если для всех аргументов я, ИКСИ (F1) не хуже схемы преобразования, чем ICSi (F2), а затем

- для некоторого аргумента j ICSj (F1) является лучшей последовательностью преобразования, чем ICSj (F2), или, если не это, [... ]

Таким образом, оператор-const -qualified Member будет всегда лучше, чем const -qualified, даже если преобразование результата точное на последнем.

+0

Хорошо, тогда это имеет смысл! Большое спасибо за перезапись стандарта, чтобы сделать его более читаемым :) – chouquette

6

Соответствующие правила определены в [over.match.best]:

Учитывая эти определения, жизнеспособная функция F1 определяется быть лучше, чем другой функция жизнеспособной функции F2, если для всех аргументов я, ИКС я (F1) не хуже, чем последовательность преобразования ICS я (F2), а затем
(1.3) - для некоторого аргумента J, ИКС J (F1) является лучшей последовательностью преобразования, чем ICS J (F2), или, если не то, что,
(1.4) - контекст инициализации с помощью пользовательского преобразования (см 8.5, 13.3 .1.5 и 13.3.1.6) и стандартную последовательность преобразования из возвращаемого типа F1 в тип назначения (то есть тип инициализированного объекта ) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из возвращаемого типа F2 до места назначения.

Давайте рассмотрим первый случай bool. У нас есть два жизнеспособных кандидатов:

Test::operator T<bool>() const; 
Test::operator Foo*(); 

Оба называются с непостоянным constTest. Для второй перегрузки конверсии не нужны - последовательность преобразования - это просто точное соответствие. Однако для первой перегрузки неявный аргумент this должен пройти квалификационное преобразование от Test до const Test. Таким образом, вторая перегрузка является предпочтительной - мы не переходим ко второму этапу, в котором обсуждается тип возврата.

Если мы сбросили const однако, жизнеспособные кандидаты стали:

Test::operator T<bool>(); 
Test::operator Foo*(); 

Здесь, оба кандидата одинаково жизнеспособный с идентичными последовательностями преобразования, но bool шаблона является предпочтительным, так как последовательность преобразования из типа возвращаемого bool до bool (Идентичность - наивысший ранг) является лучшей последовательностью преобразования, чем от Foo* до bool (Boolean Conversion - самый низкий).

+0

Большое спасибо за объяснения! – chouquette

Смежные вопросы