Я обнаружил несоответствие в том, как современные компиляторы C ++ (clang / gcc) определяют, зависит ли имя. В следующем примере A::f
зависит, но ::f
нет, что приводит к ошибке при использовании последнего.
template<typename>
struct B
{
typedef int Type;
};
template<typename U>
static U f(U u);
template<typename T>
struct A
{
template<typename U>
static U f(U u);
typename B<decltype(f(0))>::Type m1; // typename required
B<decltype(::f(0))>::Type m2; // typename not required
};
Непоследовательная часть заключается в том, что декларация A::f
не зависит от параметра шаблона A
Это означает, что нет необходимости рассматривать его как зависимое имя.
Такое поведение, по-видимому, охватывается следующей формулировкой в стандарте C ++ 11:
[Temp.dep.expr] / 3Выражение id зависит от типа, если оно содержит
[Temp.dep.type] / 3
- идентификатор, связанный с поиском имени с одним или несколькими объявлениями, объявленными с зависимым типом
Тип зависит, если он
- составной тип, построенный из любого зависимого типа
Декларация ::f
явно не зависит, так как его тип зависит только от его собственных параметров шаблона. Почему должен A::f
относиться по-другому?
Я думаю, что на основе стандарта, f
на самом деле не зависит
14.6.2.2 Типозависимые выражения [temp.dep.expr]
3 Ан ID-выражение зависит от типа, если он содержит
- идентификатор связанный с поиском имени с одним или несколькими объявлениями, объявленными с зависимым типом,
Это в равной степени относится к глобальной функции шаблона, как и к функции шаблона члена: совсем нет. Тип возврата U
зависит от определений функций шаблона, но для вызывающей стороны тип функции f<int>
уже был преобразован из U(U)
в int(int)
, В любом случае, это не объясняет, почему компиляторы обрабатывают два случая по-разному, а также не объясняет, почему функция-член, не являющаяся шаблоном, также рассматривается как зависимая.
- Шаблон-идентификатор это зависит,
- преобразование-функция-идентификатор который определяет зависимый тип, или
Это не относится. Здесь нет <
или же >
который всегда должен присутствовать в Шаблон-идентификатор, и не вызывается функция преобразования.
- вложенное имя спецификатор или Квалифицированный-идентификатор который называет члена неизвестной специализации;
Увидеть ниже.
или если он называет статический член данных текущего экземпляра, который имеет тип «массив неизвестной границы
T
» для некоторыхT
(14.5.1.3).
Это также не относится: нет задействованных массивов.
Так что это зависит от того, f
является членом неизвестной специализации. Но это не так:
14.6.2.1 Зависимые типы [temp.dep.type]
5 Имя это член неизвестной специализации если это
- Квалифицированный-идентификатор в котором […].
- Квалифицированный-идентификатор в котором […].
- ID-выражение обозначает член в выражении доступа к члену класса (5.2.5), в котором […].
Они не могут применяться: f
не является ни квалифицированным, ни частью выражения доступа члена класса.
Так как единственный способ f
может быть зависимым, если он является членом неизвестной специализации, и он не является членом неизвестной специализации, f
не должен быть зависимым.
Что касается того, почему компиляторы все же относятся к нему как к зависимому, у меня нет ответа. Либо какая-то часть моего ответа здесь неверна, компиляторы имеют ошибки, либо компиляторы следуют другой версии стандарта C ++. Тестирование на примере, который работает независимо от того, являются ли имена зависимыми, показывает несколько вариантов обработки компилятором:
#include <cstdio>
void f(const char *s, ...) { std::printf("%s: non-dependent\n", s); }
struct S1 { };
template <typename T>
struct S2 {
static S1 a;
static S1 b() { return {}; }
template <typename U>
static U c() { return {}; }
static void z() {
f("S1()", S1()); // sanity check: clearly non-dependent
f("T()", T()); // sanity check: clearly dependent
f("a", a); // compiler agreement: non-dependent
f("b()", b()); // compiler disagreement: dependent according to GCC 4.8, non-dependent according to clang
f("c<T>()", c<T>()); // sanity check: clearly dependent
f("c<S1>()", c<S1>()); // compiler agreement: dependent
f("decltype(b())()", decltype(b())()); // compiler agreement: dependent
}
};
void f(const char *s, S1) { std::printf("%s: dependent\n", s); }
// Just to show it's possible to specialize the members
// without specializing the full template.
template <>
S1 S2<S1>::b() { return {}; }
template <>
template <>
S1 S2<S1>::c<S1>() { return {}; }
int main() {
S2<S1>::z();
}
Эта разница в лечении Clang b()
, decltype(b())()
, а также c<S1>()
Это особенно беспокоит меня. Это просто не имеет никакого смысла. Они явно все одинаково зависимы. Я могу понять с точки зрения реализации, что нужно позаботиться о том, чтобы не генерировать код для функций-членов только потому, что могут быть специализации S2<S1>::b
или же S2<S1>::c<S1>
, но это относится ко всем и не влияет на тип возвращаемого значения.