17 Templates [temp]

17.6 Name resolution [temp.res]

В определении шаблона можно использовать три вида имен:

  • Имя самого шаблона и имена, объявленные в самом шаблоне.

  • Имена, зависящие от template-parameter([temp.dep]).

  • Имена из областей видимости в определении шаблона.

Предполагается, что имя, используемое в объявлении или определении шаблона и зависящее от a, template-parameter не именует тип, если только применимый поиск имени не найдет имя типа или имя не уточнено ключевым словом typename. [Example:

// no B declared here

class X;

template<class T> class Y {
  class Z;                      // forward declaration of member class

  void f() {
    X* a1;                      // declare pointer to X
    T* a2;                      // declare pointer to T
    Y* a3;                      // declare pointer to Y<T>
    Z* a4;                      // declare pointer to Z
    typedef typename T::A TA;
    TA* a5;                     // declare pointer to T's A
    typename T::A* a6;          // declare pointer to T's A
    T::A* a7;                   // T​::​A is not a type name:
                                // multiplication of T​::​A by a7; ill-formed, no visible declaration of a7
    B* a8;                      // B is not a type name:
                                // multiplication of B by a8; ill-formed, no visible declarations of B and a8
  }
};

end example]

Если a qualified-idпредназначен для ссылки на тип, который не является членом текущего экземпляра ([temp.dep.type]), и nested-name-specifier относится к зависимому типу, он должен иметь префикс в виде ключевого слова typename, образующего typename-specifier. Если qualified-idв a typename-specifier не обозначен тип или шаблон класса, программа имеет неправильный формат.

typename-specifier:
	typename nested-name-specifier identifier
	typename nested-name-specifier templateopt simple-template-id

Если специализация шаблона создается для набора template-arguments таких, что qualified-id префикс typename не обозначает тип или шаблон класса, специализация сформирована неправильно. Обычный qualified name lookup используется для поиска qualified-id даже при наличии typename. [Example:

struct A {
  struct X { };
  int X;
};
struct B {
  struct X { };
};
template<class T> void f(T t) {
  typename T::X x;
}
void foo() {
  A a;
  B b;
  f(b);             // OK: T​::​X refers to B​::​X
  f(a);             // error: T​::​X refers to the data member A​::​X not the struct A​::​X
}

end example]

Полное имя, используемое в качестве имени в a class-or-decltype или an elaborated-type-specifier , неявно предполагается для наименования типа без использования typename ключевого слова. В, nested-name-specifierкоторый непосредственно содержит, nested-name-specifier который зависит от параметра шаблона, неявно предполагается , что identifierили simple-template-idдля имени типа, без использования typename ключевого слова. [ Ключевое слово не допускается синтаксисом этих конструкций. ]Note: typenameend note

Если для заданного набора аргументов шаблона создается экземпляр специализации шаблона, который ссылается на a, qualified-id который обозначает тип или шаблон класса, и qualified-idссылается на член неизвестной специализации, то qualified-idдолжен быть либо предварен префиксом, typename либо должен быть используется в контексте, в котором он неявно называет тип, как описано выше. [Example:

template <class T> void f(int i) {
  T::x * i;         // T​::​x must not be a type
}

struct Foo {
  typedef int x;
};

struct Bar {
  static int const x = 5;
};

int main() {
  f<Bar>(1);        // OK
  f<Foo>(1);        // error: Foo​::​x is a type
}

end example]

В определении шаблона класса или в определении члена шаблона класса, следующего за declarator-id, ключевое слово typename не требуется при ссылке на имя ранее объявленного члена шаблона класса, который объявляет тип или шаблон класса. [ Note: Такие имена можно найти с помощью функции unqualified name lookupпоиска члена класса ([class.qual]) в current instantiationвыражении доступа к члену класса или поиска ([basic.lookup.classref]), когда тип выражения объекта является текущим экземпляром ([temp.dep.expr]). ] [end noteExample:

template<class T> struct A {
  typedef int B;
  B b;              // OK, no typename required
};

end example]

Зная, какие имена являются именами типов, можно проверить синтаксис каждого шаблона. Программа составлена ​​неправильно, диагностика не требуется, если:

  • никакая допустимая специализация не может быть сгенерирована для шаблона или части constexpr if оператора в шаблоне, и шаблон не создается, или

  • для каждой допустимой специализации вариативного шаблона требуется пустой пакет параметров шаблона, или

  • гипотетическая реализация шаблона сразу после его определения будет неправильно сформирована из-за конструкции, которая не зависит от параметра шаблона, или

  • интерпретация такой конструкции в гипотетическом экземпляре отличается от интерпретации соответствующей конструкции в любом фактическом создании экземпляра шаблона. [ Note: Это может произойти в следующих ситуациях:

    • тип, используемый в независимом имени, является неполным в точке, в которой определен шаблон, но завершен в точке, в которой выполняется создание экземпляра, или

    • поиск имени в определении шаблона нашел a using-declaration, но поиск в соответствующей области в экземпляре не нашел никаких объявлений, потому что это using-declaration было расширение пакета, а соответствующий пакет пуст, или

    • создание экземпляра использует аргумент по умолчанию или аргумент шаблона по умолчанию, который не был определен в точке, в которой был определен шаблон, или

    • constant expression evaluation в экземпляре шаблона использует

      • значение const объекта целочисленного или перечислимого типа без области видимости, или

      • стоимость constexpr объекта или

      • значение ссылки или

      • определение функции constexpr,

      и этот объект не был определен при определении шаблона, или

    • специализация шаблона класса или специализация шаблона переменной, которая указана независимым simple-template-id, используется шаблоном, и либо он создается из частичной специализации, которая не была определена при определении шаблона, либо он называет явную специализацию, которая не была объявлена когда шаблон был определен.

    end note]

В противном случае для шаблона, для которого может быть сгенерирована действительная специализация, не будет выдаваться никакая диагностика. [ Note: Если создается экземпляр шаблона, ошибки будут диагностироваться в соответствии с другими правилами этого международного стандарта. Когда именно диагностируются эти ошибки, возникает проблема качества реализации. ] [end noteExample:

int j;
template<class T> class X {
  void f(T t, int i, char* p) {
    t = i;          // diagnosed if X​::​f is instantiated, and the assignment to t is an error
    p = i;          // may be diagnosed even if X​::​f is not instantiated
    p = j;          // may be diagnosed even if X​::​f is not instantiated
  }
  void g(T t) {
    +;              // may be diagnosed even if X​::​g is not instantiated
  }
};

template<class... T> struct A {
  void operator++(int, T... t);                     // error: too many parameters
};
template<class... T> union X : T... { };            // error: union with base class
template<class... T> struct A : T...,  T... { };    // error: duplicate base class

end example]

При поиске объявления имени, используемого в определении шаблона, обычные правила поиска ([basic.lookup.unqual], [basic.lookup.argdep]) используются для независимых имен. Поиск имен, зависящих от параметров шаблона, откладывается до тех пор, пока не станет известен фактический аргумент шаблона ([temp.dep]). [Example:

#include <iostream>
using namespace std;

template<class T> class Set {
  T* p;
  int cnt;
public:
  Set();
  Set<T>(const Set<T>&);
  void printall() {
    for (int i = 0; i<cnt; i++)
      cout << p[i] << '\n';
  }
};

в примере i - это локальная переменная, i объявленная в printall, cnt это член, cnt объявленный в Set, и cout это стандартный поток вывода, объявленный в iostream. Однако не все объявления можно найти таким образом; разрешение некоторых имен должно быть отложено до тех пор, пока не template-arguments станут известны действительные . Например, даже несмотря на то, что имя operator<< известно в определении, printall() и его объявление можно найти в <iostream>, фактическое объявление, operator<< необходимое для печати, p[i] не может быть известно, пока не станет известно, что это за тип T ([temp.dep]). ]end example

Если имя не зависит от template-parameter (как определено в [temp.dep]), объявление (или набор объявлений) для этого имени должно быть в области видимости в точке, где имя появляется в определении шаблона; имя привязано к объявлению (или объявлениям), найденному в этой точке, и на эту привязку не влияют объявления, видимые в момент создания экземпляра. [Example:

void f(char);

template<class T> void g(T t) {
  f(1);             // f(char)
  f(T(1));          // dependent
  f(t);             // dependent
  dd++;             // not dependent; error: declaration for dd not found
}

enum E { e };
void f(E);

double dd;
void h() {
  g(e);             // will cause one call of f(char) followed by two calls of f(E)
  g('a');           // will cause three calls of f(char)
}

end example]

[ Note: В целях поиска имени аргументы по умолчанию, а также noexcept-specifiers шаблоны функций, аргументы по умолчанию и noexcept-specifiers функции-члены шаблонов классов считаются определениями ([temp.decls]). ]end note

17.6.1 Locally declared names [temp.local]

Как и обычные (не шаблонные) классы, шаблоны классов имеют расширение injected-class-name. Введенное-имя-класса может использоваться как template-nameили type-name. Когда он используется с template-argument-list, как template-argumentдля шаблона template-parameterили как окончательный идентификатор в elaborated-type-specifierобъявлении шаблона дружественного класса, он ссылается на сам шаблон класса. В противном случае, это равносильно тому , template-name сопровождаемое template-parameters шаблон класса , заключенном в <>.

В рамках специализации шаблона класса или частичной специализации, когда имя injected-class-name используется как a type-name, оно эквивалентно, за template-nameкоторым следует template-arguments специализация шаблона класса или частичная специализация, заключенная в <>. [Example:

template<template<class> class T> class A { };
template<class T> class Y;
template<> class Y<int> {
  Y* p;                               // meaning Y<int>
  Y<char>* q;                         // meaning Y<char>
  A<Y>* a;                            // meaning A<​::​Y>
  class B {
    template<class> friend class Y;   // meaning ​::​Y
  };
};

end example]

Нагнетаемого класса имя шаблона класса или класса специализации шаблона может быть использован либо как template-nameили type-name там , где она находится в области видимости. [Example:

template <class T> struct Base {
  Base* p;
};

template <class T> struct Derived: public Base<T> {
  typename Derived::Base* p;    // meaning Derived​::​Base<T>
};

template<class T, template<class> class U = T::template Base> struct Third { };
Third<Base<int> > t;            // OK: default argument uses injected-class-name as a template

end example]

Поиск, который находит внедренное имя-класса ([class.member.lookup]), в некоторых случаях может привести к неоднозначности (например, если он найден более чем в одном базовом классе). Если все найденные имена внедренных классов относятся к специализациям одного и того же шаблона класса, и если имя используется как a template-name, ссылка относится к самому шаблону класса, а не к его специализации, и не является двусмысленной. [Example:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
  typename Derived::Base b;             // error: ambiguous
  typename Derived::Base<double> d;     // OK
};

end example]

Когда используется обычное имя шаблона (т. Е. Имя из охватывающей области, а не внедренное имя-класса), оно всегда относится к самому шаблону класса, а не к его специализации. [Example:

template<class T> class X {
  X* p;             // meaning X<T>
  X<T>* p2;
  X<int>* p3;
  ::X* p4;          // error: missing template argument list
                    // ​::​X does not refer to the injected-class-name
};

end example]

A template-parameter не следует повторно объявлять в пределах своей области действия (включая вложенные области действия). Имя A template-parameter не должно совпадать с именем шаблона. [Example:

template<class T, int i> class Y {
  int T;            // error: template-parameter redeclared
  void f() {
    char T;         // error: template-parameter redeclared
  }
};

template<class X> class X;      // error: template-parameter redeclared

end example]

В определении члена шаблона класса, который появляется вне определения шаблона класса, имя члена шаблона класса скрывает имя template-parameter любого включающего шаблона класса (но не template-parameterчлена, если член является шаблон класса или функции). [Example:

template<class T> struct A {
  struct B { /* ... */ };
  typedef void C;
  void f();
  template<class U> void g(U);
};

template<class B> void A<B>::f() {
  B b;              // A's B, not the template parameter
}

template<class B> template<class C> void A<B>::g(C) {
  B b;              // A's B, not the template parameter
  C c;              // the template parameter C, not A's C
}

end example]

В определении члена шаблона класса, который появляется за пределами пространства имен, содержащего определение шаблона класса, имя template-parameter скрывает имя члена этого пространства имен. [Example:

namespace N {
  class C { };
  template<class T> class B {
    void f(T);
  };
}
template<class C> void N::B<C>::f(C) {
  C b;              // C is the template parameter, not N​::​C
}

end example]

В определении шаблона класса или в определении члена такого шаблона, который появляется вне определения шаблона, для каждого независимого базового класса ([temp.dep.type]), если имя базового класса или имя члена базовый класс такой же , как название template-parameter, имя базового класса или члена имя hides на template-parameter имя. [Example:

struct A {
  struct B { /* ... */ };
  int a;
  int Y;
};

template<class B, class a> struct X : A {
  B b;              // A's B
  a b;              // error: A's a isn't a type name
};

end example]

17.6.2 Dependent names [temp.dep]

Внутри шаблона некоторые конструкции имеют семантику, которая может отличаться от одного экземпляра к другому. Такая конструкция зависит от параметров шаблона. В частности, типы и выражения могут зависеть от типа и / или значения параметров шаблона (как определено аргументами шаблона), и это определяет контекст для поиска имени для определенных имен. Выражения могут быть type-dependent (то есть его тип может зависеть от параметра шаблона) или value-dependent (то есть его значение при оценке как постоянное выражение ([expr.const]) может зависеть от параметра шаблона), как описано в этом подпункте. В выражении формы:

postfix-expression ( expression-listopt )

где postfix-expression является an unqualified-id, unqualified-id обозначает a, dependent name если

Если операнд оператора является выражением, зависящим от типа, оператор также обозначает зависимое имя. Такие имена не связаны и просматриваются в точке создания экземпляра шаблона ([temp.point]) как в контексте определения шаблона, так и в контексте точки создания экземпляра.

[Example:

template<class T> struct X : B<T> {
  typename T::A* pa;
  void f(B<T>* pb) {
    static int i = B<T>::i;
    pb->j++;
  }
};

имя базового класса, имя B<T>типа T​::​A, имена B<T>​::​i и pb->j явно зависят от template-parameter. ]end example

В определении класса или шаблона класса область видимости dependent base class не проверяется во время неквалифицированного поиска имени ни в точке определения шаблона или члена класса, ни во время создания экземпляра шаблона или члена класса. [Example:

typedef double A;
template<class T> class B {
  typedef int A;
};
template<class T> struct X : B<T> {
  A a;              // a has type double
};

Имя типа A в определении X<T> связывается с именем typedef, определенным в области глобального пространства имен, а не с именем typedef, определенным в базовом классе B<T>. ] [end exampleExample:

struct A {
  struct B { /* ... */ };
  int a;
  int Y;
};

int a;

template<class T> struct Y : T {
  struct B { /* ... */ };
  B b;                          // The B defined in Y
  void f(int i) { a = i; }      // ​::​a
  Y* p;                         // Y<T>
};

Y<A> ya;

Члены A​::​B, A​::​aи A​::​Y аргумента шаблона A не влияют на привязку имен в Y<A>. ]end example

17.6.2.1 Dependent types [temp.dep.type]

Имя относится к тому, current instantiation если это

  • в определении шаблона класса, вложенного класса шаблона класса, члена шаблона класса или члена вложенного класса шаблона injected-class-name класса, шаблона класса или вложенного класса,

  • в определении шаблона первичного класса или члена шаблона первичного класса, имя шаблона класса, за которым следует список аргументов шаблона первичного шаблона (как описано ниже), заключенный в <> (или эквивалентную специализацию псевдонима шаблона),

  • в определении вложенного класса шаблона класса имя вложенного класса, на который ссылается как на член текущего экземпляра, или

  • в определении частичной специализации или члена частичной специализации - имя шаблона класса, за которым следует список аргументов шаблона частичной специализации, заключенной в <> (или эквивалентная специализация псевдонима шаблона). Если параметр nth шаблона является пакетом параметров, nаргумент шаблона th является pack expansion шаблоном, шаблон которого является именем пакета параметров.

Список аргументов шаблона первичного шаблона - это список аргументов шаблона, в котором nаргумент th шаблона имеет значение параметра nth шаблона шаблона класса. Если nth параметром шаблона является a template parameter pack, nаргумент th шаблона - это a pack expansion , шаблон которого является именем пакета параметров шаблона.

Аргумент шаблона, который эквивалентен параметру шаблона (т. Е. Имеет то же постоянное значение или тот же тип, что и параметр шаблона), может использоваться вместо этого параметра шаблона в ссылке на текущий экземпляр. В случае аргумента шаблона, не являющегося типом, аргументу должно быть задано значение параметра шаблона, а не выражение, в котором параметр шаблона появляется как часть выражения. [Example:

template <class T> class A {
  A* p1;                        // A is the current instantiation
  A<T>* p2;                     // A<T> is the current instantiation
  A<T*> p3;                     // A<T*> is not the current instantiation
  ::A<T>* p4;                   // ​::​A<T> is the current instantiation
  class B {
    B* p1;                      // B is the current instantiation
    A<T>::B* p2;                // A<T>​::​B is the current instantiation
    typename A<T*>::B* p3;      // A<T*>​::​B is not the current instantiation
  };
};

template <class T> class A<T*> {
  A<T*>* p1;                    // A<T*> is the current instantiation
  A<T>* p2;                     // A<T> is not the current instantiation
};

template <class T1, class T2, int I> struct B {
  B<T1, T2, I>* b1;             // refers to the current instantiation
  B<T2, T1, I>* b2;             // not the current instantiation
  typedef T1 my_T1;
  static const int my_I = I;
  static const int my_I2 = I+0;
  static const int my_I3 = my_I;
  B<my_T1, T2, my_I>* b3;       // refers to the current instantiation
  B<my_T1, T2, my_I2>* b4;      // not the current instantiation
  B<my_T1, T2, my_I3>* b5;      // refers to the current instantiation
};

end example]

A dependent base class - это базовый класс, который является зависимым типом и не является текущим экземпляром. [ Note: Базовый класс может быть текущим экземпляром в случае, если вложенный класс называет включающий класс базовым. [Example:

template<class T> struct A {
  typedef int M;
  struct B {
    typedef void M;
    struct C;
  };
};

template<class T> struct A<T>::B::C : A<T> {
  M m;                          // OK, A<T>​::​M
};

end example] ]end note

Имя - это member of the current instantiation если это

  • Неквалифицированное имя, которое при поиске относится по крайней мере к одному члену класса, который является текущим экземпляром или его независимым базовым классом. [ Note: Это может произойти только при поиске имени в области, содержащейся в определении шаблона класса. ] end note

  • A, qualified-id в котором nested-name-specifier относится к текущему экземпляру и который при поиске относится по крайней мере к одному члену класса, который является текущим экземпляром, или его независимым базовым классом. [ Note: Если такой член не найден, а у текущего экземпляра есть какие-либо зависимые базовые классы, то qualified-idэто член неизвестной специализации; см. ниже. ] end note

  • id-expressionОбозначая член в class member access выражении для которого тип выражения объекта является текущим конкретизации, и id-expression, когда looked up, относится к по меньшей мере , одного члена класса , который является текущим конкретизации или не зависящих от их базового класса. [ Note: Если такой член не найден, а у текущего экземпляра есть какие-либо зависимые базовые классы, то id-expressionэто член неизвестной специализации; см. ниже. ] end note

[Example:

template <class T> class A {
  static const int i = 5;
  int n1[i];                    // i refers to a member of the current instantiation
  int n2[A::i];                 // A​::​i refers to a member of the current instantiation
  int n3[A<T>::i];              // A<T>​::​i refers to a member of the current instantiation
  int f();
};

template <class T> int A<T>::f() {
  return i;                     // i refers to a member of the current instantiation
}

end example]

Имя - это, dependent member of the current instantiation если он является членом текущего экземпляра, который при поиске ссылается по крайней мере на один член класса, который является текущим экземпляром.

Имя - это member of an unknown specialization если это

  • Объект, qualified-id в котором nested-name-specifier именуется зависимый тип, не являющийся текущим экземпляром.

  • A, qualified-idв котором nested-name-specifier относится к текущему экземпляру, текущий экземпляр имеет по крайней мере один зависимый базовый класс, а поиск имени qualified-idне находит ни одного члена класса, который является текущим экземпляром или его независимым базовым классом.

  • id-expressionОбозначая член в class member access выражении , в котором либо

    • тип объектного выражения - это текущая реализация, текущая реализация имеет по крайней мере один зависимый базовый класс, а поиск имени id-expressionне находит члена класса, который является текущим экземпляром, или его независимого базового класса; или

    • тип объектного выражения является зависимым и не является текущим экземпляром.

Если a, qualified-idв котором nested-name-specifier относится к текущему экземпляру, не является членом текущего экземпляра или элементом неизвестной специализации, программа имеет неправильный формат, даже если шаблон, содержащий элемент, не создается qualified-id; диагностика не требуется. Точно так же, если id-expressionвыражение доступа к члену класса, для которого тип выражения объекта является текущим экземпляром, не относится к члену текущего экземпляра или члену неизвестной специализации, программа имеет неправильный формат, даже если шаблон содержащее выражение доступа к члену не создается; диагностика не требуется. [Example:

template<class T> class A {
  typedef int type;
  void f() {
    A<T>::type i;               // OK: refers to a member of the current instantiation
    typename A<T>::other j;     // error: neither a member of the current instantiation nor
                                // a member of an unknown specialization
  }
};

end example]

Если для заданного набора аргументов шаблона создается экземпляр специализации шаблона, который ссылается на член текущего экземпляра с qualified-idвыражением доступа к члену класса or, имя в qualified-idвыражении доступа к члену класса or ищется в экземпляре шаблона. контекст. Если результат этого поиска отличается от результата поиска имени в контексте определения шаблона, поиск имени неоднозначен. [Example:

struct A {
  int m;
};

struct B {
  int m;
};

template<typename T>
struct C : A, T {
  int f() { return this->m; }   // finds A​::​m in the template definition context
  int g() { return m; }         // finds A​::​m in the template definition context
};

template int C<B>::f();     // error: finds both A​::​m and B​::​m
template int C<B>::g();     // OK: transformation to class member access syntax
                            // does not occur in the template definition context; see [class.mfct.non-static]

end example]

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

  • параметр шаблона,

  • сотрудник неизвестной специализации,

  • вложенный класс или перечисление, которое является зависимым членом текущего экземпляра,

  • тип cv-qualified, где cv-unqualified тип является зависимым,

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

  • тип массива, тип элемента которого является зависимым или чья граница (если есть) зависит от значения,

  • тип функции, спецификация исключения которой зависит от значения,

  • a, simple-template-id в котором либо имя шаблона является параметром шаблона, либо любой из аргументов шаблона является зависимым типом или выражением, которое зависит от типа или значения, или является расширением пакета [ Note: Сюда входит injected-class-name шаблон класса, используемый без расширения template-argument-list. ] , или end note

  • обозначается , где есть . decltype(expression)expression type-dependent

[ Note: Поскольку typedef не вводит новые типы, а вместо этого просто ссылается на другие типы, имя, которое ссылается на typedef, который является членом текущего экземпляра, зависит только в том случае, если тип, на который ссылается, является зависимым. ]end note

17.6.2.2 Type-dependent expressions [temp.dep.expr]

За исключением случаев, описанных ниже, выражение зависит от типа, если какое-либо подвыражение зависит от типа.

this зависит от типа, если тип класса включающей функции-члена зависит ([temp.dep.type]).

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

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

  • identifier , связанный с именем поиска с не-типа template-parameter объявлена с типом , который содержит placeholder type,

  • identifier, связанный с именем поиска с одним или более деклараций функций членов текущего экземпляра объявленных с типом возвращаемого , который содержит тип заполнителя,

  • identifier, связанный с именем поиском с structured binding declaration которого brace-or-equal-initializerявляется типом-зависимым,

  • identifier _­_­func_­_­, где любая вмещающих функция представляет собой шаблон, член шаблона класса, или общая лямбду,

  • template-id , что зависит,

  • a, conversion-function-id который указывает зависимый тип, или

  • a nested-name-specifier или a, в qualified-id котором упоминается член неизвестной специализации;

или если он называет зависимый член текущего экземпляра, который является статическим элементом данных типа «массив неизвестных границ T» для some T ([temp.static]). Выражения следующих форм зависят от типа, только если тип, указанный с помощью type-id, simple-type-specifier или new-type-id является зависимым, даже если любое подвыражение зависит от типа:

simple-type-specifier ( expression-listopt )
::opt new new-placementopt new-type-id new-initializeropt
::opt new new-placementopt ( type-id ) new-initializeropt
dynamic_cast < type-id > ( expression )
static_cast < type-id > ( expression )
const_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
( type-id ) cast-expression

Выражения следующих форм никогда не зависят от типа (поскольку тип выражения не может быть зависимым):

literal
postfix-expression . pseudo-destructor-name
postfix-expression -> pseudo-destructor-name
sizeof unary-expression
sizeof ( type-id )
sizeof ... ( identifier )
alignof ( type-id )
typeid ( expression )
typeid ( type-id )
::opt delete cast-expression
::opt delete [ ] cast-expression
throw assignment-expressionopt
noexcept ( expression )

[ Note: Для стандартного библиотечного макроса offsetofсм [support.types].. ]end note

A class member access expression зависит от типа, если выражение относится к члену текущего экземпляра, а тип указанного члена является зависимым, или если выражение доступа к члену класса относится к члену неизвестной специализации. [ Note: В выражении формы x.y или xp->y типа выражения обычно является тип члена y класса x (или класса, на который указывает xp). Однако, если x или xp относится к зависимому типу, который не является текущим экземпляром, тип y всегда является зависимым. Если x или xp относится к независимому типу или относится к текущему экземпляру, тип y является типом выражения доступа к члену класса. ]end note

A braced-init-listзависит от типа, если какой-либо элемент зависит от типа или является расширением пакета.

A fold-expressionзависит от типа.

17.6.2.3 Value-dependent expressions [temp.dep.constexpr]

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

An id-expression зависит от значения, если:

  • это зависит от типа,

  • это имя параметра шаблона, не являющегося типом,

  • он называет статический член данных, который является зависимым членом текущего экземпляра и не инициализируется в a member-declarator,

  • он называет статическую функцию-член, которая является зависимым членом текущего экземпляра, или

  • это константа литерального типа и инициализируется выражением, зависящим от значения.

Выражения следующей формы зависят от значения, если unary-expressionили expression зависит от типа, или type-id зависит:

sizeof unary-expression
sizeof ( type-id )
typeid ( expression )
typeid ( type-id )
alignof ( type-id )
noexcept ( expression )

[ Note: Для стандартного библиотечного макроса offsetofсм [support.types].. ]end note

Выражения следующей формы являются значением-зависимой , если либо type-id или simple-type-specifier зависит , или , expression или cast-expression является значением в зависимости от:

simple-type-specifier ( expression-listopt )
static_cast < type-id > ( expression )
const_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
( type-id ) cast-expression

Выражения следующей формы зависят от значения:

sizeof ... ( identifier )
fold-expression

Выражение формы, в которой имена зависимых членов текущего экземпляра зависят от значения. Выражение вида также является значением-зависимым , если оценивать как успешно , и результат оценки относится к шаблонному объекту , который является объектом с длительностью статической или потоком хранения или функцией - члена. &qualified-idqualified-id &cast-expressioncast-expression core constant expression

17.6.2.4 Dependent template arguments [temp.dep.temp]

Тип template-argument является зависимым, если тип, который он указывает, является зависимым.

Не-тип template-argument является зависимым, если его тип является зависимым или константное выражение, которое он задает, зависит от значения.

Кроме того, не-тип template-argument является зависимым, если соответствующий не-тип template-parameter относится к типу ссылки или указателя, а template-argument обозначает или указывает на член текущего экземпляра или член зависимого типа.

Шаблон template-argument является зависимым, если он называет template-parameter или qualified-id относится к члену неизвестной специализации.

17.6.3 Non-dependent names [temp.nondep]

Независимые имена, используемые в определении шаблона, обнаруживаются с помощью обычного поиска по именам и привязываются к месту их использования. [Example:

void g(double);
void h();

template<class T> class Z {
public:
  void f() {
    g(1);           // calls g(double)
    h++;            // ill-formed: cannot increment function; this could be diagnosed
                    // either here or at the point of instantiation
  }
};

void g(int);        // not in scope at the point of the template definition, not considered for the call g(1)

end example]

17.6.4 Dependent name resolution [temp.dep.res]

При разрешении зависимых имен учитываются имена из следующих источников:

  • Объявления, которые видны в момент определения шаблона.

  • Объявления из пространств имен, связанных с типами аргументов функции, как из контекста создания ([temp.point]), так и из контекста определения.

17.6.4.1 Point of instantiation [temp.point]

Для специализации шаблона функции, специализации шаблона функции-члена или специализации для функции-члена или статического члена данных шаблона класса, если специализация создается неявно, поскольку на нее имеется ссылка из другой специализации шаблона и контекста, из которого она указанная ссылка зависит от параметра шаблона, точка создания экземпляра специализации - это точка создания экземпляра включающей специализации. В противном случае точка создания такой специализации следует сразу за объявлением или определением области пространства имен, которое ссылается на специализацию.

Если шаблон функции или функция-член шаблона класса вызывается способом, который использует определение аргумента по умолчанию для этого шаблона функции или функции-члена, точкой создания экземпляра аргумента по умолчанию является точка создания экземпляра шаблона функции или специализация функций-членов.

Для noexcept-specifierспециализации шаблона функции или специализации функции-члена в шаблоне класса, если noexcept-specifierнеявно создается экземпляр, потому что он необходим другой специализации шаблона, и контекст, который требует этого, зависит от параметра шаблона, точкой создания экземпляра noexcept-specifierявляется точка реализации требуемой специализации. В противном случае точка создания такого объекта noexcept-specifierсразу следует за объявлением или определением области пространства имен, для которых требуется noexcept-specifier.

Для специализации шаблона класса, специализации шаблона члена класса или специализации для члена класса шаблона класса, если специализация создается неявно, потому что на нее ссылаются из другой специализации шаблона, если контекст, из которого делается ссылка на специализацию, зависит в параметре шаблона, и если специализация не создается до создания экземпляра включающего шаблона, точка создания экземпляра находится непосредственно перед точкой создания экземпляра включающего шаблона. В противном случае точка создания такой специализации непосредственно предшествует объявлению или определению области пространства имен, которое ссылается на специализацию.

Если виртуальная функция создается неявно, ее точка создания экземпляра следует сразу же за точкой создания экземпляра ее специализации шаблона включающего класса.

Явное определение экземпляра - это точка создания экземпляра для специализации или специализаций, указанных с помощью явного создания экземпляра.

Контекст создания выражения, который зависит от аргументов шаблона, представляет собой набор объявлений с внешней связью, объявленных до момента создания экземпляра специализации шаблона в той же единице преобразования.

Специализация для шаблона функции, шаблона функции-члена или функции-члена или статического члена данных шаблона класса может иметь несколько точек создания экземпляров в единице перевода, и в дополнение к точкам создания экземпляров, описанным выше, для любых таких специализации, которая имеет точку создания в пределах единицы перевода, конец единицы трансляции также считается точкой создания экземпляра. Специализация для шаблона класса имеет не более одной точки создания экземпляра в единице перевода. Специализация для любого шаблона может иметь точки реализации в нескольких единицах перевода. Если две разные точки создания экземпляра придают специализации шаблона разные значения в зависимости от one-definition rule, программа имеет неправильный формат и диагностика не требуется.

17.6.4.2 Candidate functions [temp.dep.candidate]

Для вызова функции, где postfix-expression- зависимое имя, функции-кандидаты находятся с использованием обычных правил поиска ([basic.lookup.unqual], [basic.lookup.argdep]), за исключением того, что:

  • Для части использования поиска unqualified name lookupнайдены только объявления функций из контекста определения шаблона.

  • Для части поиска с использованием связанных пространств имен ([basic.lookup.argdep]) обнаруживаются только объявления функций, найденные либо в контексте определения шаблона, либо в контексте создания экземпляра шаблона.

Если вызов был бы неправильно сформирован или нашел бы лучшее совпадение, если бы при поиске в связанных пространствах имен учитывались все объявления функций с внешней связью, введенные в этих пространствах имен во всех единицах перевода, а не только с учетом этих объявлений, найденных в определении шаблона и шаблоне контекстов создания экземпляров, тогда программа имеет неопределенное поведение.

17.6.5 Friend names declared within a class template [temp.inject]

Дружественные классы или функции могут быть объявлены в шаблоне класса. Когда создается экземпляр шаблона, имена его друзей обрабатываются так, как если бы специализация была явно объявлена ​​в точке создания экземпляра.

Как и в случае с классами, не являющимися шаблонами, имена дружественных функций области видимости пространства имен специализации шаблона класса не видны во время обычного поиска, если явно не объявлены в области видимости пространства имен ([class.friend]). Такие имена можно найти в правилах для связанных классов ([basic.lookup.argdep]).140 [Example:

template<typename T> struct number {
  number(int);
  friend number gcd(number x, number y) { return 0; };
};

void g() {
  number<double> a(3), b(4);
  a = gcd(a,b);     // finds gcd because number<double> is an associated class,
                    // making gcd visible in its namespace (global scope)
  b = gcd(3,4);     // ill-formed; gcd is not visible
}

end example]

Объявления друзей не вводят новые имена ни в какую область видимости ни при объявлении шаблона, ни при его создании.