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

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

Такое поведение, по-видимому, охватывается следующей формулировкой в ​​стандарте С++ 11:

[temp.dep.expr]/3

Идентификационное выражение зависит от типа, если оно содержит

  • идентификатор, связанный поиском имени с одним или несколькими объявлениями, объявленными с зависимым типом

[temp.dep.type]/3

Тип зависим, если он

  • составной тип, созданный из любого зависимого типа

Объявление ::f явно не зависимо, так как его тип зависит только от собственных параметров шаблона. Почему к A::f следует относиться по-другому?


person willj    schedule 26.12.2014    source источник
comment
A::f эквивалентно A<T>::f, что может зависеть от параметра шаблона T. Так почему же это удивительно?   -  person Pradhan    schedule 27.12.2014
comment
Чтобы быть зависимым, имя должно быть членом неизвестной специализации. A<T> зависит, но также и текущая реализация означает, что A<T>::f, следовательно, не является членом неизвестной специализации.   -  person willj    schedule 27.12.2014
comment
Может быть другая перегрузка A::f в другой специализации A<T>.   -  person 0x499602D2    schedule 27.12.2014
comment
@ 0x499602D2 Да, но поскольку для этого требуется специализация A<T>, это не может вызвать никакой разницы в значении везде, где f(0) используется в неспециализированном A<T>.   -  person    schedule 27.12.2014
comment
Но будьте осторожны: правило состоит не в том, что ошибки, связанные с независимыми типами, должны диагностироваться во время определения шаблона, а в том, что шаблон, который никогда не может иметь допустимого экземпляра, может быть диагностирован во время определения шаблона. Обработка компилятора будет разной. Некоторые могут подавлять ошибки даже для выражений, не зависящих от типа, другие могут диагностировать некоторые ошибки даже для выражений, зависящих от типа. (Не то чтобы я говорю, что это так или не так.)   -  person    schedule 27.12.2014
comment
typename запрещено? Это на самом деле невозможно, так как С++ 11.   -  person Columbo    schedule 27.12.2014


Ответы (1)


Я думаю, что на основе стандарта f на самом деле не зависит.

14.6.2.2 Выражения, зависящие от типа [temp.dep.expr]

3 id-expression зависит от типа, если оно содержит

  • идентификатор, связанный поиском имени с одним или несколькими объявлениями, объявленными с зависимым типом,

Это относится как к функции глобального шаблона, так и к функции шаблона-члена: никоим образом. Тип возвращаемого значения U зависит от определений функций шаблона, но для вызывающего объекта тип функции f<int> уже был преобразован из U(U) в int(int). Во всяком случае, это не объясняет, почему компиляторы по-разному обрабатывают эти два случая, а также не объясняет, почему нешаблонные функции-члены также рассматриваются как зависимые.

  • зависимый идентификатор шаблона,
  • идентификатор функции преобразования, указывающий зависимый тип, или

Это не применимо. Не существует < или >, которые всегда должны присутствовать в идентификаторе шаблона, и нет вызываемой функции преобразования.

  • описатель вложенного имени или полный идентификатор, который называет члена неизвестной специализации;

Смотри ниже.

или если он называет статический элемент данных текущего экземпляра, который имеет массив типов неизвестной границы T для некоторого T (14.5.1.3).

Это также не относится: здесь не задействованы никакие массивы.

Так что это зависит от того, является ли f членом неизвестной специализации. Но это не так:

14.6.2.1 Зависимые типы [temp.dep.type]

5 Имя является членом неизвестной специализации, если оно

  • квалифицированный идентификатор, в котором [...].
  • квалифицированный идентификатор, в котором [...].
  • id-expression, обозначающий член в выражении доступа к члену класса (5.2.5), в котором [...].

Они не могут применяться: f не является ни квалифицированным, ни частью выражения доступа к члену класса.

Поскольку f может быть зависимым только в том случае, если он является членом неизвестной специализации и не является членом неизвестной специализации, f не должен быть зависимым.

На вопрос, почему компиляторы все же считают его зависимым, у меня нет ответа. Либо какая-то часть моего ответа здесь неверна, либо у компиляторов есть ошибки, либо компиляторы следуют другой версии стандарта С++. Тестирование на примере, который работает независимо от того, являются ли имена зависимыми, показывает несколько вариантов обработки компилятором:

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

person Community    schedule 27.12.2014
comment
Да, это основная аргументация, которую я тоже имел в виду. Должны ли мы писать отчеты об ошибках? - person Columbo; 27.12.2014
comment
Я бы предпочел сначала провести дополнительное расследование, по крайней мере, проверить самые последние версии компиляторов, и если они все еще рассматривают его как зависимый, посмотреть, есть ли какая-либо документация, объясняющая почему или объясняющая, что это известная проблема. Я бы только после этого сообщил об этом как об ошибке. Но если вы считаете иначе, не стесняйтесь сообщать об этом напрямую. - person ; 27.12.2014
comment
Мы имеем дело с самыми последними стабильными версиями GCC и Clang. Я мог бы протестировать транк Clang SVN и так далее, но я считаю, что они дают вам эквивалентные результаты. Мы также можем задать вопрос в группе ISO C++ Google. - person Columbo; 27.12.2014
comment
@Columbo Я думаю, что тест, который я сейчас включил в свой ответ, показывает по крайней мере одну определенную ошибку в clang. Нет никакого смысла иметь b() независимую, а decltype(b()) зависимую. Независимо от других примеров, это само по себе гарантирует ошибку IMO. - person ; 27.12.2014
comment
Сообщается как llvm.org/bugs/show_bug.cgi?id=22038. Не тестировался с достаточно новой версией GCC, чтобы сообщить им об этом. - person ; 27.12.2014