2013-02-20 2 views
12

Рассмотрим следующий простой примерПерегрузки преобразование шаблона оператор

struct C 
{ 
    template <typename T> operator T() {return 0.5;} 
    operator int() {return 1;} 
    operator bool() {return false;} 
}; 

int main() 
{ 
    C c; 
    double x = c; 
    std::cout << x << std::endl; 
} 

При компиляции с Clang, он дает следующую ошибку

test.cpp:11:12: error: conversion from 'C' to 'double' is ambiguous 
    double x = c; 
     ^ ~ 
test.cpp:4:5: note: candidate function 
    operator int() {return 1;} 
    ^
test.cpp:5:5: note: candidate function 
    operator bool() {return false;} 
    ^
test.cpp:3:27: note: candidate function [with T = double] 
    template <typename T> operator T() {return 0.5;} 
         ^
1 error generated. 

Другие компиляторы генерировать подобные ошибки, например, GCC и Intel ICLC

Если я удалю operator int и operator bool. Он отлично компилируется и работает, как ожидалось. Если удалить только один из них, то есть сохранить оператор шаблона и сказать operator int, тогда всегда выбирается версия без шаблонов.

Мое понимание заключается в том, что только в случае, когда перегруженные функции шаблона и не шаблона равны в том смысле, что они оба идеально подходят или оба требуют одной и той же последовательности преобразований, предпочтительной будет версия без шаблонов. Однако в этом случае кажется, что компилятор не видит шаблон оператора как идеальное совпадение. И когда присутствуют перегрузки bool и int, то, естественно, они считают, что они неоднозначны.

Итак, мой вопрос в том, почему шаблон оператора не считается идеальным совпадением в этом случае?

ответ

4

Это интересно. Существует два способа прочитать критическую часть раздела 13.3.3.Исходный пример должен обязательно вызвать шаблон функции, но версия, в которой удаляется один из не-шаблонов, может считаться неоднозначным.

13.3.3:

жизнеспособная функция F1 определяется быть лучше функция, чем другой жизнеспособной функции F2, если для всех аргументов я, ICS_i (F1) не хуже последовательность преобразования чем ICS_i (F2), а затем

  • для некоторого аргумента J, ICS_j (F1) является лучшей, чем последовательность преобразования ICS_j (F2), или, если не то, что,

  • контекст инициализации с помощью пользовательского преобразования (см 8.5, 13.3.1.5, 13.3.1.6 и) и стандартная последовательность преобразования из возвращаемого типа F1 в тип назначения (то есть тип инициализируемого объекта) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из возвращаемого типа F2 в тип назначения или, если это не так,

  • F1 - это не шаблонная функция, а F2 - это специализированная функция, или, если нет,

  • F1 и F2 являются функции шаблона специализации, а шаблон функции для F1 более специализированы, чем шаблон для F2 в соответствии с частичными правилами порядка, описанные в 14.5.6.2.

Если есть только одна жизнеспособная функция, которая является лучшей функцией, чем все другие жизнеспособные функции, то она выбирается с помощью разрешения перегрузки; в противном случае звонок плохо сформирован.

В примере, лязг правильно определяет набор из трех жизнеспособных функций-кандидатов:

C::operator int() 
C::operator bool() 
C::operator double<double>() 

Третий шаблон функции специализация. (Я не думаю, что синтаксис выше является законным, но вы понимаете: в этот момент разрешения перегрузки он не рассматривается как шаблон, а как специализация с определенным типом функции.)

Единственное неявное преобразование Последовательность аргументов здесь (ICS1) является точной совпадением «lvalue C» на «C&» на неявный параметр, так что это не изменит ситуацию.

Этот пример - это точно ситуация, описанная во второй пуле, поэтому функция, возвращающая double, явно лучше, чем две другие.

Вот где это странно: по буквальному чтению, operator int также лучше, чем специализация шаблона, из-за третьей пули. «Подождите минуту, не следует« лучше »быть антисимметричным? Как вы можете сказать, F1 лучше, чем F2 И F2 лучше, чем F1?» К сожалению, Стандарт явно не говорит ничего подобного. «Разве вторая пуля не имеет приоритета над третьей пулей из-за фразы« если не эта »?"Да, для постоянного F1 и F2. Но стандарт не сказать, что удовлетворяющее второй пули для (F1,F2) делает третью пулю (F2,F1) не применяется.

Конечно, так как operator int не лучше, чем operator bool и наоборот, существует еще одна «жизнеспособная функция», которая является лучшей функцией, чем все другие жизнеспособные функции ».

Я не совсем одобряю это странное чтение, за исключением, возможно, сообщить об этом в качестве стандартного дефекта. Идти с этим было бы странно последствия (удаление перегрузка не лучший из этого примера изменяет программу от хорошо сформированной до двусмысленной!). Я думаю, что цель состоит в том, чтобы вторая пуля была рассмотрена в обоих направлениях до того, как третья пуля будет рассмотрена вообще.

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

+0

Ваше «странное чтение» стандарта очень интересно и на самом деле имеет для меня очень много смысла. Если я правильно ее понимаю, то ли оператор int лучше, чем шаблон, зависит от F1 и F2. Если мы рассмотрим F1 = op int, F2 = op , то при пуле 3 мы определим F1 лучше, чем F2 и остановимся. Но если мы рассмотрим F1 = op , F2 = op int, то на марке 2 мы лучше определим F1 и остановимся. Но если их три, то op int и op bool уже не лучше других, и, следовательно, нет двусмысленных –

6

Давайте разберем это на две разные проблемы:

1. Почему это генерировать ошибку компилятора?

struct C 
{ 
    operator bool() {return false;} 
    operator int() {return 1;} 
}; 

Поскольку оба int и bool может быть неявно преобразован в double, компилятор не может знать, какие функции он должен использовать. Есть две функции, которые он может использовать, и ни один из них не имеет приоритета над другим.

2. Почему нет шаблонной версии идеального соответствия?

struct C 
{ 
    template <typename T> operator T() {return 0.5;} 
    operator int() {return 1;} 
}; 

Почему operator int() вызывается при запросе двойной?

Функция без шаблона вызывается потому, что функция без шаблона имеет преимущество при разрешении перегрузки. (Overloading function templates)

EDIT: я был неправ! Как отметил Ян Чжоу в своем комментарии, и, как указано в ссылке, которую я предоставил, идеальное совпадение в шаблонной функции имеет приоритет над нетемпликационной функцией.

Я проверил ваш код (скомпилирован с g ++ 4.7.2), и он работал как ожидалось: он вернул 0.5, другими словами, была использована шаблонная функция!

EDIT2: Теперь я попытался с clang, и я могу воспроизвести описанное вами поведение. Поскольку он корректно работает в gcc, это кажется ошибкой в ​​clang.

+0

Первая часть, которую я уже понял, как указано в вопросе. Однако вторая часть, не-шаблонная функция имеет преимущественную силу при разрешении перегрузки, когда другие аспекты равны. В этом случае шаблон не требует преобразования, он возвращает double, а оператор без шаблона возвращает int или bool, а затем его нужно преобразовать в double. Поэтому я до сих пор не понимаю, почему это не идеальное совпадение. Даже в ссылке, которую вы даете, второй вызов 'f' показывает, что шаблон идеального совпадения имеет приоритет. Non-template имеют приоритет - первый и третий вызов –

+0

@YanZhou вы правы, я обновил свой ответ. Но мое приложение выглядит по-другому, чем ваше. Я тестировал его с помощью gcc, и в настоящее время я устанавливаю clang для сравнения. – Misch

+0

Какая версия GCC вы использовали? Я пробовал G ++ 4.7, и он дает ту же ошибку, что и Clang, так же как и Intel icpc 13.1 –

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