2009-07-24 6 views
10

С учетом приведенного ниже кода, почему выбрана функция foo(T*)?Каковы правила выбора из перегруженных функций шаблона?

Если удалить его (foo(T*)) код по-прежнему компилируется и работает правильно, но ++ v4.4.0 G (и, возможно, другие компиляторы, а) будет генерировать два foo() функции: один для полукокса [4] и один для полукокса [ 7].

#include <iostream> 
using namespace std; 

template< typename T > 
void foo(const T&) 
{ 
    cout << "foo(const T&)" << endl; 
} 

template< typename T > 
void foo(T*) 
{ 
    cout << "foo(T*)" << endl; 
} 

int main() 
{ 
    foo("bar"); 
    foo("foobar"); 
    return 0; 
} 
+1

Смешно, MSVC9 называет функцию «foo » в символах отладки, но, по-видимому, поскольку она признает, что реализация идентична - использует ту же реализацию для обоих вызовов. - Интересный вопрос, во всяком случае. – peterchen

+0

AFAIK VC сбрасывает экземпляры шаблонов, которые приводят к идентичному коду. – sbi

+1

GCC (4.2.4) делает как foo , так и foo экземплярами чего-либо вплоть до -O3, где он просто полностью их устраняет для этих реализаций и делает его встроенным. –

ответ

6

Формально при сравнении последовательностей преобразования lvalue-преобразования игнорируются. Переходы сгруппированы в несколько категорий, как квалификации регулировки (T* ->T const*), именующее преобразования (int[N] ->int*, void() ->void(*)()) и другие.

Единственная разница между вашими двумя кандидатами - это преобразование lvalue. Строковые литералы - это массивы, которые преобразуются в указатели. Первый кандидат принимает массив по ссылке и, следовательно, не нуждается в преобразовании lvalue. Второй кандидат требует преобразования lvalue.

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

Давайте сравним два, глядя на их подписи их список

void(T const&); 
void(T*); 

функции параметра Если мы выберем некоторый уникальный тип Q для первого списка параметров и попытаться соответствовать против второго списка параметров, мы соответствие Q против T*. Это не удастся, так как Q не является указателем. Таким образом, вторая по меньшей мере такая же специализированная, как и первая.

Если мы делаем наоборот, мы сопоставляем Q* с T const&. Ссылка отбрасывается, а квалификаторы верхнего уровня игнорируются, а оставшиеся T становятся Q*. Это точное совпадение с целью частичного упорядочивания, и, таким образом, вывод списка преобразованных параметров второго по сравнению с первым кандидатом завершается успешно. Поскольку другое направление (против второго) не увенчалось успехом, вторым кандидатом является больше, специализированный, чем первый, и, следовательно, разрешение перегрузки будет предпочтительнее второго, если в противном случае была бы двусмысленность.

В 13.3.3.2/3:

Стандартной последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если [...]

  • S1 является собственно подпоследовательностью S2 (сравнения последовательностей преобразования в канонической форме, определяемой 13.3.3.1.1, исключая любое преобразование именующего; последовательность преобразования идентичности считаются подпоследовательностью любого не -identity последовательность преобразования) или, если не то, что [...]

Тогда 13.3.3/1

  • позволяет ICSi (F) обозначать неявную последовательность преобразований, которая преобразует i-й аргумент в список в тип i-го параметра жизнеспособной функции F. 13.3.3.1 определяет последовательности неявного преобразования и 13.3.3.2 определяет, что это означает, что одна неявная последовательность преобразований является лучшей последовательностью преобразования или хуже последовательностью преобразования, чем другая.

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

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

Наконец, вот таблица неявных преобразований, которые могут участвовать в стандартной последовательности преобразования в 13.3.3.1.1/3.

Conversion sequences http://img259.imageshack.us/img259/851/convs.png

+0

Это НЕ относится к предпочтениям, отличным от шаблонов, потому что функция-не-шаблон не будет выбрана компилятором. Дело в том, что const char [] гораздо ближе соответствует T *, чем T & (поскольку const char [] и const char * эквивалентны). –

+0

извините, я упустил шаблон <...> пункт перед вторым. fixed :) –

+0

Ваше утверждение о том, что совпадение с 'T *' ближе, неверно, хотя :) –

-1

Причина "" - это символ *, который идеально подходит для функции foo (T *). Когда вы удалите это, компилятор попытается заставить его работать с foo (T &), в котором вам необходимо передать ссылку на массив символов, содержащий строку.

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

+0

Неправильно тоже. Пожалуйста, не догадывайтесь. '' '' является 'char const [1]'. Вы также не можете «передавать ссылку». Выражения всегда имеют не ссылочный тип. –

+0

Я никогда не говорил, что это не так (на самом деле это «", это const [2]), я имел в виду, что использование функции T * предпочтительнее, если оно существует. Что касается оператора 'pass ref', я пытался сказать, что менее предпочтительным вариантом является T &, что в основном является ссылкой на T (в данном случае ссылкой на char [N]), хотя мой ответ не звучал вполне правильно, ваше право с этим. Исправьте меня, если я ошибаюсь (и после этого я просто удалю свой пост, поскольку он не полезен по сравнению с вашим, что намного более полно). –

0

на основе правил разрешения перегрузки (Приложение B из C++ Templates: The Complete Guide имеет хороший обзор), строковые литералы (Const обугленные []) ближе к Т *, чем T &, потому что компилятор не делает различий между полукоксом [] и char *, поэтому T * является ближайшим совпадением (const T * будет точным соответствием).

В самом деле, если вы могли бы добавить:

template<typename T> 
void foo(const T[] a) 

(который вы не можете), ваш компилятор скажет вам, что эта функция является переопределение:

template<typename T> 
void foo(const T* a) 
+0

'T *' отнюдь не является более близким. Попробуйте 'void f (char const (&)[1]); void f (char const *); int main() {f (" ");}': Этот вызов, который напоминает ситуацию выше, используя не-шаблоны, является неоднозначным. –

+0

@ litb: T * намного ближе, чем T & в этом случае. Да, если вы включаете определенный SIZE (& [N]), то это делает его неоднозначным, но T * соответствует произвольному массиву гораздо более близко, чем T &. –

+0

@Nick, в то время как void foo (T x []) является синонимом void foo (T * x), это не означает, что вы не можете создавать void foo (T &), когда T является типом массива.Эквивалент void foo (ArrayBaseType (&) [ArraySize]) – AProgrammer

2

Полный ответ является весьма техническим.

Во-первых, струнные литералы char const[N] type.

Затем происходит неявное преобразование от char const[N] до char const*.

Таким образом, ваша функция шаблона совпадает, одна с использованием ссылки, использующая неявное преобразование. Когда они останутся одни, обе ваши функции шаблонов могут обрабатывать вызовы, но когда они оба присутствуют, мы должны объяснить, почему второе foo (созданное с помощью T = char const [N]) является лучшим совпадением, чем первое (экземпляр с T = char).Если вы посмотрите на правила перегрузки (как указано на LITB), выбор между

void foo(char const (&x)[4)); 

и

void foo(char const* x); 

Неоднозначный (правила являются довольно сложными, но вы можете проверить, написав письменный шаблон функции с такие сигнатуры и видеть, что компилятор жалуется). В этом случае выбор делается во втором, потому что он более специализирован (опять же правила для этого частичного упорядочения сложны, но в этом случае это связано с тем, что вы можете передать char const[N] в char const*, но не char const*, чтобы char const[N] так же, как void bar(char const*) более специализирован, чем void bar(char*), потому что вы можете пройти char* до char const*, но не наоборот.

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