Ответ 1
Короткий ответ: всякий раз, когда ссылается на вложенное имя, которое является зависимым именем, т.е. вложенным внутри экземпляра шаблона с неизвестным параметром.
Длинный ответ: в С++ есть три уровня сущностей: значения, типы и шаблоны. Все они могут иметь имена, и только одно имя не говорит вам, какой уровень сущности он есть. Скорее, информация о природе объекта имени должна быть выведена из контекста.
Всякий раз, когда этот вывод невозможен, вы должны указать его:
template <typename> struct Magic; // defined somewhere else
template <typename T> struct A
{
static const int value = Magic<T>::gnarl; // assumed "value"
typedef typename Magic<T>::brugh my_type; // decreed "type"
// ^^^^^^^^
void foo() {
Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template"
// ^^^^^^^^
}
};
Здесь должны были быть указаны имена Magic<T>::gnarl
, Magic<T>::brugh
и Magic<T>::kwpq
, потому что невозможно сказать: поскольку Magic
является шаблоном, сама природа типа Magic<T>
зависит от T
- могут быть, например, специализации, которые полностью отличаются от основного шаблона.
Что делает Magic<T>::gnarl
зависимым именем тот факт, что мы находимся внутри определения шаблона, где T
неизвестно. Если бы мы использовали Magic<int>
, это было бы иначе, поскольку компилятор знает (вы обещаете!) Полное определение Magic<int>
.
(Если вы хотите проверить это самостоятельно, вот примерное определение Magic
, которое вы можете использовать. Простите использование constexpr
для специализации для краткости, если у вас старый компилятор, не стесняйтесь изменять статическое членное объявление константы в старом стиле pre-С++ 11.)
template <typename T> struct Magic
{
static const T gnarl;
typedef T & brugh;
template <typename S> static void kwpq(int, char, double) { T x; }
};
template <> struct Magic<signed char>
{
// note that `gnarl` is absent
static constexpr long double brugh = 0.25; // `brugh` is now a value
template <typename S> static int kwpq(int a, int b) { return a + b; }
};
Использование:
int main()
{
A<int> a;
a.foo();
return Magic<signed char>::kwpq<float>(2, 3); // no disambiguation here!
}