Разрешение перегрузки шаблонов функций в VS2013 RC

Компилирование следующего кода в VS2012 без проблем.

struct Foo
{
typedef int DummyType;
};

template<typename T>
int Bar(T* foo, typename T::DummyType* dummy_ = 0) { return 0; }

template<typename T>
int Bar(T* foo, ...) { return 1; }

template<typename T>
int Bar(typename T::DummyType* dummy_ = 0) { return 2; }

template<typename T>
int Bar(...) { return 3; }void fn()
{
Bar((Foo*)NULL);
Bar((int*)NULL);

Bar<Foo>();
Bar<int>();
}

Но попытка VS2013RC получила следующие ошибки. Это ошибка VS2013RC или проблема с самим кодом. Что говорится в стандарте о функции перегрузки соответствия, специализации функций шаблона и функций с переменным числом аргументов.

1>c:\users\dummy\documents\visual studio 2013\projects\test\test.cpp(25): error C2668: 'Bar' : ambiguous call to overloaded function
1>          c:\users\dummy\documents\visual studio 2013\projects\test\test.cpp(15): could be 'int Bar<Foo>(T *,...)'
1>          with
1>          [
1>              T=Foo
1>          ]
1>          c:\users\dummy\documents\visual studio 2013\projects\test\test.cpp(12): or       'int Bar<Foo>(T *,Foo::DummyType *)'
1>          with
1>          [
1>              T=Foo
1>          ]
1>          while trying to match the argument list '(Foo *)'
1>c:\users\dummy\documents\visual studio 2013\projects\test\test.cpp(28): error C2668: 'Bar' : ambiguous call to overloaded function
1>          c:\users\dummy\documents\visual studio 2013\projects\test\test.cpp(21): could be 'int Bar<Foo>(...)'
1>          c:\users\dummy\documents\visual studio 2013\projects\test\test.cpp(18): or       'int Bar<Foo>(Foo::DummyType *)'
1>          while trying to match the argument list '()'

Спасибо за любую помощь!


Спасибо за ответ!

Я только что сделал новый тест следующим образом:

struct Foo
{
typedef int DummyType;
};

// Bar0 #0
template<typename T>
static int Bar0(const T* foo, typename T::DummyType* dummy_) { return 0; }

// Bar0 #1
template<typename T>
static int Bar0(const T* foo, ...) { return 1; }template<typename T, typename U>
struct DummyType2 {};

// Bar1 #2
template<typename T>
static int Bar1(const T* foo, DummyType2<T, typename T::DummyType>* dummy_) { return 2; }

// Bar1 #3
template<typename T>
static int Bar1(const T* foo, ...) { return 3; }

void fn()
{

std::cout<<Bar0((Foo*)NULL, NULL)<<std::endl;   // call 0 matches Bar0 #0

std::cout<<Bar1((Foo*)NULL, NULL)<<std::endl;   // call 1 matches Bar1 #3
}

выход

0
3

По какой причине вызов 0 соответствует Bar0 # 0, но вызов 1 соответствует Bar1 # 3. Какие-нибудь правила из стандартных?

0

Решение

Нумерация этих четырех перегрузок для справки:

template<typename T>
int Bar(T*, typename T::DummyType* = 0);             // #1

template<typename T>
int Bar(T*, ...);                                    // #2

template<typename T>
int Bar(typename T::DummyType* = 0);                 // #3

template<typename T>
int Bar(...);                                        // #4

Согласно [temp.deduct.type] / 5, typename T::DummyType является не выводимым контекстом для T, Т.е. параметр typename T::DummyType* dummy_ не может быть использован для вывода T, Поэтому для первых двух звонков

Bar((Foo*)NULL);
Bar((int*)NULL);

T может быть выведено для первых двух перегрузок, но не для вторых двух. Вот почему перегрузки № 3 и № 4 не являются жизнеспособными для этих вызовов. После этого вычета каждый случай T в функции сигнатура будет заменена на выводимый тип. Это может привести к сбою замены, см. Вызов 2 ниже.


Для первого вызова возможны следующие две перегрузки:

/*substituted template*/
int Bar<Foo>(Foo*, Foo::DummyType* = 0);             // #1

/*substituted template*/
int Bar<Foo>(Foo*, ...);                             // #2

Согласно [over.match.viable] / 2, аргументы по умолчанию игнорируются для разрешения перегрузки:

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

  • Если есть м аргументы в списке, все функции-кандидаты имеют точно м параметры являются жизнеспособными.
  • Функция-кандидат, имеющая меньше чем м Параметры являются жизнеспособными только в том случае, если в их списке параметров есть многоточие (8.3.5). В целях разрешения перегрузки любой аргумент, для которого нет соответствующего параметра, считается «соответствующим многоточию» (13.3.3.1.3).
  • Функция-кандидат, имеющая более м Параметры являются жизнеспособными, только если (М + 1)-Параметр st имеет аргумент по умолчанию (8.3.6). В целях разрешения перегрузки список параметров обрезается справа, так что м параметры.

Итак, мы фактически сравниваем эти две подписи здесь:

/*substituted template*/
int Bar<Foo>(Foo*);                                 // #1

/*substituted template*/
int Bar<Foo>(Foo*);                                 // #2

Эти два ранга как точные совпадения и, следовательно, неоднозначны.


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

/*substituted template*/
int Bar<int>(int*);                                 // #2

Ошибка замены:

Для второго звонка Bar((int*)NULL);, замена T за int приводит к ошибке замены при первой перегрузке [temp.deduct] / 5:

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

Недопустимый тип здесь int::DummyType,


Для третьего и четвертого вызова жизнеспособны только последние две перегрузки (из-за количества аргументов). Остальное похоже на первые две перегрузки.

Третий звонок должен выбрать из перегрузок

/*substituted template*/
int Bar<Foo>(Foo::DummyType* = 0);                   // #3

/*substituted template*/
int Bar<Foo>(...);                                   // #4

что неоднозначно, как первый звонок.


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


Дополнительный вопрос.

Первый звонок: Bar0((Foo*)NULL, NULL)

Перегрузки:

// Bar0 #0
template<typename T>
static int Bar0(const T* foo, typename T::DummyType* dummy_);

// Bar0 #1
template<typename T>
static int Bar0(const T* foo, ...);

В Bar0 # 0, T опять-таки в не выводимом контексте, поэтому для вывода используется только первый аргумент. Подставленные шаблонные подписи выглядят так:

// substituted template
static int Bar0<Foo>(const Foo*, Foo::DummyType*);  // #0

// substituted template
static int Bar0<Foo>(const Foo* foo, ...);          // #1

Определение NULL теперь становится несколько актуальным:

[Support.types] / 3

Макрос NULL является определенной в реализации константой нулевого указателя C ++ в этом международном стандарте.

[Conv.ptr] / 1

константа нулевого указателя является целочисленным константным выражением (5.19) prvalue целочисленного типа, который оценивается как ноль, или prvalue типа std::nullptr_t, Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа и отличается от любого другого значения указателя объекта или типа указателя на функцию.

Точный тип NULL не указано (еще одна причина для использования nullptr вместо!). Но мы знаем, что он конвертируется в Foo::DummyType*, Это преобразование стандартное преобразование. согласование NULL с многоточием это так называемый эллипсис; на самом деле это не преобразование, только с точки зрения разрешения перегрузки [over.ics.ellipsis] / 1:

Последовательность преобразования многоточия происходит, когда аргумент в вызове функции соответствует спецификации параметра многоточия вызываемой функции (см. 5.2.2).

Теперь две перегрузки жизнеспособны и должны быть ранжированы. К счастью, здесь все просто [over.ics.rank] / 2

стандартная последовательность преобразования представляет собой лучшую последовательность преобразования, чем определенная пользователем последовательность преобразования или последовательность преобразования с многоточием

Следовательно, последовательность преобразования тип NULL в Foo::DummyType*как требуется для перегрузки # 0, это лучшая последовательность преобразования по сравнению с последовательностью преобразования многоточия для сопоставления NULL с ... перегрузки № 2.

[over.match.best] теперь указывает, что выбрана функция с наилучшей последовательностью преобразования. Следовательно, перегрузка № 0 выбрана однозначно.


Второй звонок: Bar1((Foo*)NULL, NULL)

Перегрузки:

// Bar1 #2
template<typename T>
static int Bar1(const T*, DummyType2<T, typename T::DummyType>*);

// Bar1 #3
template<typename T>
static int Bar1(const T*, ...);

Здесь важной частью является T в DummyType2<T, ..>, это не в не выводимом контексте. Поэтому компилятор пытается вывести T от первой а также Второй аргумент. Поскольку второй аргумент в вызове имеет некоторый неопределенный интеграл или std::nullptr_t тип, вывод типа не удается из-за перегрузки Bar1 # 2. перегрузка Bar1 № 3 остается жизнеспособным и однозначно выбран.

Однако если вы измените перегрузку Bar1 № 2 для:

// Bar1 #2
template<typename T>
static int Bar1(const T*, DummyType2<int, typename T::DummyType>*);

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

Вы также можете (вместо изменения перегрузки) изменить второй вызов на:

Bar1((Foo*)NULL, (DummyType2<Foo, int>*)NULL)

Сюда, T может быть однозначно выведен Fooи перегрузка Bar1 № 2 выбран.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector