Является ли тип функции зависимым, если он зависит только от собственных параметров шаблона?

Я обнаружил несоответствие в том, как современные компиляторы 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 относиться по-другому?

4

Решение

Я думаю, что на основе стандарта, 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>, но это относится ко всем и не влияет на тип возвращаемого значения.

3

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


По вопросам рекламы [email protected]