Вывод типа для `T :: some_typredef` в шаблонной функции - PullRequest
3 голосов
/ 06 июля 2011

Имеется функция шаблона, которая не использует параметр шаблона для непосредственного ввода.Как будет работать вывод типа C ++?Например, задано

template<typename T>
void f(T::value_type){}

когда (если вообще) будет ли вывод для этой функции работать?

Есть ли другое место, кроме template<typename T1,...>void f(T1,T2,...), где может произойти вывод типа?

Как всегда, приведите стандарт для дополнительного кредита.

Ответы [ 4 ]

6 голосов
/ 06 июля 2011

Он никогда не работает для вложенного типа, если только T не является параметром.

Если вы позвоните f(1), компилятор не сможет найти все Т с вложенным typedef int value_type;.

Вы можете вывести тип, который является частью типа параметра, например

template<class T>
void f(std::vector<T>);
5 голосов
/ 06 июля 2011

Я думаю, что вот ваш ответ:

14.8.2.4 - Вывод аргументов шаблона из типа [temp.deduct.type]

[...]

-3- [...]

В большинстве случаев типы, шаблоны и нетиповые значения, используемые для составления P, участвуют в выводе аргументов шаблона. То есть они могут использоваться для определения значения аргумента шаблона, и определенное таким образом значение должно соответствовать значениям, определенным в другом месте. Однако в определенных контекстах значение не участвует в выводе типа, а вместо этого использует значения аргументов шаблона, которые были либо выведены в другом месте, либо явно указаны. Если параметр шаблона используется только в не выводимых контекстах и ​​не указан явно, вывод аргумента шаблона завершается неудачей.

-4- Неопределенные контексты:

  • Спецификатор вложенного имени типа, указанного с помощью qualid-id .

  • Тип, который представляет собой template-id , в котором один или несколько из template-arguments является выражением, которое ссылается на template-параметр .

Когда имя типа указывается способом, который включает в себя не выводимый контекст, все типы, составляющие это имя типа, также не выводятся. Однако составной тип может включать как выведенные, так и не выведенные типы. [ Пример : если тип указан как A<T>::B<T2>, то T и T2 не выводятся. Аналогично, если тип указан как A<I+J>::X<T>, I, J и T не выводятся. Если тип указан как void f(A<T>::B, A<T>), T в A<T>::B не выводится, а T в A<T> выводится. ]

Ваш T::value_type является квалифицированным идентификатором типа, поэтому типы в его спецификаторе вложенного имени не выводятся и должны быть указаны явно.

Редактировать: эта информация взята из ИСО / МЭК 14882: 1998 .

0 голосов
/ 06 июля 2011

Если вы не укажете параметр T, компилятор не будет пытаться вычесть T каким-либо образом.

Даже если вы укажете это явно, я считаю, что это будет работать только там, где предоставляется оригинальное определение, но не typedefs.

Рассмотрим следующий пример (скомпилированный с clang ++, по-видимому, g ++ на этом не работает):

#include <stdio.h>
template <typename T>
void foo(T) {
printf("foo(T)\n");
}

template <typename T>
void foo(typename T::value) {
printf("foo(T::value)\n");
}

struct X {
  class value {};
};

struct Z {
  typedef int value;
};

struct XZ {
  typedef Z value;
};

typedef X::value Xv;

#define CALL(function,param) printf(#function " (" #param ") = "); function(param());

void explicitCalls() {
  printf("Explicit calls:\n");
  CALL(foo<int>,int);
  CALL(foo<X::value>,X::value);
  CALL(foo<Z::value>,Z::value);
  CALL(foo<XZ::value>,XZ::value);
  CALL(foo<Xv>,Xv);
}

void implicitCalls() {
  printf("Implicit calls:\n");
  CALL(foo,int);
  CALL(foo,X::value);
  CALL(foo,Z::value);
  CALL(foo,XZ::value);
  CALL(foo,Xv);
}

int main() {
  explicitCalls();
  implicitCalls();
}

Вывод:

Explicit calls:
foo<int> (int) = foo(T)
foo<X::value> (X::value) = foo(T::value)
foo<Z::value> (Z::value) = foo(T)
foo<XZ::value> (XZ::value) = foo(T)
foo<Xv> (Xv) = foo(T::value)
Implicit calls:
foo (int) = foo(T)
foo (X::value) = foo(T)
foo (Z::value) = foo(T)
foo (XZ::value) = foo(T)
foo (Xv) = foo(T)
0 голосов
/ 06 июля 2011

Стандарт требует устранения неоднозначности зависимого типа:

template<typename T>
void f(typename T::value_type){}

В прошлом в этой области было несколько менее стандартное поведение, в результате чего код компилировался на одном (MSVC), но не на другом (GCC) компиляторе. В наши дни, вероятно, под влиянием стандартных стандартных библиотек, таких как Boost, компиляторы, похоже, принимают только правильный код.


Ключевое слово typename иногда необходимо для устранения неоднозначности идентификаторов в зависимости от аргументов шаблона (также this ). Подумайте об этом следующим образом: вы должны предоставить компилятору достаточно информации, чтобы выполнить проверку синтаксиса в первый раз при анализе определения шаблона. Фактические аргументы шаблона в то время не известны, и (в C ++ имеется грамматика), вы должны дать подсказке компилятору, какой символ токен будет представлять позже

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...