Spec-Zone.ru › C++

Зависимые имена

Внутри определения шаблона (как шаблона класса, так и шаблона функции) значение некоторых конструкций может отличаться от одной инстанциации к другой. В частности, типы и выражения могут зависеть от типов параметров шаблонов типов и значений параметров шаблонов нетипового типа.

template<typename T>
struct X : B<T> // "B<T>" is dependent on T
{
    typename T::A* pa; // "T::A" is dependent on T
                       // (see below for the meaning of this use of "typename")
 
    void f(B<T>* pb)
    {
        static int i = B<T>::i; // "B<T>::i" is dependent on T
        pb->j++; // "pb->j" is dependent on T
    }
};

Поиск имени и привязка отличаются для зависимых имён и независимых имён.

Правила привязки

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

#include <iostream>
 
void g(double) { std::cout << "g(double)\n"; }
 
template<class T>
struct S
{
    void f() const
    {
        g(1); // "g" is a non-dependent name, bound now
    }
};
 
void g(int) { std::cout << "g(int)\n"; }
 
int main()
{
    g(1);  // calls g(int)
 
    S<int> s;
    s.f(); // calls g(double)
}

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

  • тип, используемый в независимом имени, является неполным в момент определения, но полным в момент инстанциации
  • поиск имени в определении шаблона обнаружил объявление using, но поиск в соответствующей области в инстанциации не находит никаких объявлений, потому что объявление using было расширением пакета, а соответствующий пакет пуст
(с C++17)
  • инстанциация использует аргумент по умолчанию или аргумент шаблона по умолчанию, который не был определен в точке определения
  • константное выражение в точке инстанциации использует значение объекта const целочисленного или неименованного перечислимого типа, значение объекта constexpr, значение ссылки или определение constexpr-функции(с C++11), и что объект/ссылка/функция(с C++11) не был определен в точке определения
  • шаблон использует специализацию не зависящего шаблона класса или специализацию шаблона переменной(с C++14) в момент инстанциации, и этот используемый шаблон либо инстанцируется из частичной специализации, которая не была определена в точке определения, либо называет явную специализацию, которая не была объявлена в точке определения

Привязка зависимых имён откладывается до тех пор, пока не произойдёт поиск.

Правила поиска

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

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

(другими словами, добавление нового объявления функции после определения шаблона не делает его видимым, за исключением случая ADL).

Цель этого правила состоит в том, чтобы защититься от нарушений правила ODR для инстанциаций шаблонов:

// an external library
namespace E
{
    template<typename T>
    void writeObject(const T& t)
    {
        std::cout << "Value = " << t << '\n';
    }
}
 
// translation unit 1:
// Programmer 1 wants to allow E::writeObject to work with vector<int>
namespace P1
{
    std::ostream& operator<<(std::ostream& os, const std::vector<int>& v)
    {
        for (int n : v)
            os << n << ' ';
        return os;
    }
 
    void doSomething()
    {
        std::vector<int> v;
        E::writeObject(v); // error: will not find P1::operator<<
    }
}
 
// translation unit 2:
// Programmer 2 wants to allow E::writeObject to work with vector<int>
namespace P2
{
    std::ostream& operator<<(std::ostream& os, const std::vector<int>& v)
    {
        for (int n : v)
            os << n << ':';
        return os << "[]";
    }
 
    void doSomethingElse()
    {
        std::vector<int> v;
        E::writeObject(v); // error: will not find P2::operator<<
    }
}

В приведённом выше примере, если поиск без ADL для operator<< был разрешён из контекста инстанциации, инстанциация E::writeObject<vector<int>> имела бы два различных определения: одно, использующее P1::operator<<, и другое, использующее P2::operator<<. Такое нарушение ODR может не быть обнаружено линковщиком, что приводит к использованию того или иного определения в обеих инстанциациях.

Чтобы ADL исследовал пользовательское пространство имён, либо std::vector должно быть заменено пользовательским классом, либо его тип элемента должен быть пользовательским классом:

namespace P1
{
    // if C is a class defined in the P1 namespace
    std::ostream& operator<<(std::ostream& os, const std::vector<C>& v)
    {
        for (C n : v)
            os << n;
        return os;
    }
 
    void doSomething()
    {
        std::vector<C> v;
        E::writeObject(v); // OK: instantiates writeObject(std::vector<P1::C>)
                           //     which finds P1::operator<< via ADL
    }
}

Примечание: это правило делает непрактичным перегрузку операторов для типов стандартной библиотеки:

#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
 
// Bad idea: operator in global namespace, but its arguments are in std::
std::ostream& operator<<(std::ostream& os, std::pair<int, double> p)
{
    return os << p.first << ',' << p.second;
}
 
int main()
{
    typedef std::pair<int, double> elem_t;
    std::vector<elem_t> v(10);
    std::cout << v[0] << '\n'; // OK, ordinary lookup finds ::operator<<
    std::copy(v.begin(), v.end(),
              std::ostream_iterator<elem_t>(std::cout, " "));
    // Error: both ordinary lookup from the point of definition of
    // std::ostream_iterator and ADL will only consider the std namespace,
    // and will find many overloads of std::operator<<, so the lookup will be done.
    // Overload resolution will then fail to find operator<< for elem_t
    // in the set found by the lookup.
}

Примечание: ограниченный поиск (но не привязка) зависимых имён также происходит во время определения шаблона, по мере необходимости, чтобы отличить их от независимых имён, а также определить, являются ли они членами текущей инстанциации или членами неизвестной специализации. Полученная при этом информация может использоваться для обнаружения ошибок, см. ниже.

Зависимые типы

Следующие типы являются зависимыми типами:

  • параметр шаблона
  • член неизвестной специализации (см. ниже)
  • вложенный класс/перечисление, являющийся зависимым членом неизвестной специализации (см. ниже)
  • версия типа с квалификаторами cv зависимого типа
  • составной тип, построенный из зависимого типа
  • тип массива, элемент которого является зависимым или связанное значение является значение-зависимым
  • тип функции, в параметрах которой присутствует один или несколько параметров пакета функций parameter packs
(с C++11)
  • тип функции, спецификация исключений которой зависит от значения
  • template-id, где либо
    • имя шаблона является параметром шаблона, или
    • любой из аргументов шаблона является типом-зависимым или значение-зависимым, или является расширением пакета(с C++11) (даже если template-id используется без списка аргументов, как injected-class-name)
  • результат применения decltype к выражению, зависящему от типа

Результат применения decltype к выражению, зависящему от типа, является уникальным зависимым типом. Два таких результата относятся к одному типу только если их выражения равнозначны.

(с C++11)
  • спецификатор индексации пакета, применённый к константному выражению, зависящему от типа

Спецификатор индексации пакета, применённый к константному выражению, зависящему от типа, представляет собой уникальный зависимый тип. Два таких спецификатора индексации пакета относятся к одному типу только если их константные выражения равны. В противном случае два таких спецификатора индексации пакета относятся к одному типу только если их индексы имеют одинаковое значение.

(с C++26)

Примечание: typedef-член текущей инстанциации является зависимым только тогда, когда зависимым является тип, к которому он относится.

Выражения, зависящие от типа

Следующие выражения являются зависимыми от типа:

  • выражение, любое подвыражение которого является выражением, зависящим от типа
  • this, если класс является зависимым типом.
  • выражение id, которое не является concept-id и(с C++20)
    • содержит идентификатор, для которого поиск имени находит по крайней мере одно зависимое объявление
    • содержит зависимый template-id
  • содержит специальный идентификатор __func__ (если какая-то окружающая функция является шаблоном, нешаблонным членом шаблона класса, или обобщённым лямбда-выражением(с C++14))
(с C++11)
  • содержит имя функции преобразования к зависимому типу
  • содержит квалифицированное имя или qualified-id, являющийся членом неизвестной специализации
  • называет зависимый член текущей инстанциации, который является статическим данным членом типа "массив неизвестной длины"
  • содержит идентификатор, для которого поиск имени находит одно или несколько объявлений функций текущей инстанциации, объявленных с выведением типа возвращаемого значения
(с C++14)
  • содержит идентификатор, для которого поиск имени находит объявление структурированной привязки, инициализатор которой зависит от типа
  • содержит идентификатор, для которого поиск имени находит параметр шаблона нетипового типа, тип которого содержит заполнитель auto
  • содержит идентификатор, для которого поиск имени находит переменную, объявленную с типом, который содержит тип-заполнитель (например, auto статический член данных), где инициализатор зависит от типа,
(с C++17)
  • любое выражение приведения к зависимому типу
  • new-выражение, создающее объект зависимого типа
  • выражение доступа к члену, которое ссылается на член текущей инстанциации, тип которого зависит
  • выражение доступа к члену, которое ссылается на член неизвестной специализации
  • fold-выражение
(с C++17)
  • выражение упаковки индексирования если его id-выражение является типом-зависимым выражением
(с C++26)

Примечание: литералы, псевдо-вызовы деструкторов, alignof, noexcept(с C++11), sizeof, typeid, delete и throw-выражения никогда не являются типом-зависимыми, потому что типы этих выражений не могут быть.

Значения-зависимые выражения

  • выражение, используемое в контексте, где требуется константное выражение, и любое его подвыражение является значение-зависимым
  • id-выражение, которое
  • является concept-id, и любые его аргументы являются зависимыми
(с C++20)
  • является типом-зависимым
  • является именем параметра шаблона нетипа
  • называет статическое член-данное, являющееся зависимым членом текущей инстанциации и не инициализированное.
  • называет статический член-функцию, являющуюся зависимым членом текущей инстанциации
  • является константой с типом целого или перечисления(до C++11)литерала(с C++11), инициализированной из значение-зависимого выражения
  • alignof, noexcept,(с C++11)sizeof, typeid-выражения, где аргумент является типом-зависимым выражением или зависимым type-id
  • любое выражение приведения к зависимому типу или из значение-зависимого выражения
  • адресное выражение, где аргумент — qualified-id, которое называет зависимый член текущей инстанциации
  • адресное выражение, где аргумент — любое выражение, которое, вычисленное как ядро константное выражение, ссылается на шаблонное сущность, которая является объектом со статическим или потоковым хранилищем(с C++11) продолжительностью или членом-функцией.
  • fold-выражение
(с C++17)

Зависимые имена

Текущая инстанциация

В определении шаблона класса (включая его член-функции и вложенные классы) некоторые имена могут быть выведены для ссылки на текущую инстанциацию. Это позволяет обнаружить определенные ошибки на этапе определения, а не инстанциации, и отменяет требование к typename и template разделителям для зависимых имен, см. ниже.

Только следующие имена могут ссылаться на текущую инстанциацию:

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

Аргумент шаблона эквивалентен параметру шаблона, если

  • для параметра шаблона типа аргумент шаблона обозначает тот же тип, что и параметр шаблона.
  • для параметра шаблона нетипа аргумент шаблона — идентификатор, который называет переменную, которая эквивалентна параметру шаблона. Переменная эквивалентна параметру шаблона, если
    • она имеет тот же тип, что и параметр шаблона (без учёта cv-квалификации) и
    • её инициализатор состоит из одного идентификатора, который называет параметр шаблона или, рекурсивно, такую переменную.
template<class T>
class A
{
    A* p1;      // A is the current instantiation
    A<T>* p2;   // A<T> is the current instantiation
    ::A<T>* p4; // ::A<T> is the current instantiation
    A<T*> p3;   // A<T*> is not 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<int I>
struct B
{
    static const int my_I = I;
    static const int my_I2 = I + 0;
    static const int my_I3 = my_I;
    static const long my_I4 = I;
    static const int my_I5 = (I);
 
    B<my_I>* b1;  // B<my_I> is the current instantiation:
                  //   my_I has the same type as I,
                  //   and it is initialized with only I
    B<my_I2>* b2; // B<my_I2> is not the current instantiation:
                  //   I + 0 is not a single identifier
    B<my_I3>* b3; // B<my_I3> is the current instantiation:
                  //   my_I3 has the same type as I,
                  //   and it is initialized with only my_I (which is equivalent to I)
    B<my_I4>* b4; // B<my_I4> is not the current instantiation:
                  //   the type of my_I4 (long) is not the same as the type of I (int)
    B<my_I5>* b5; // B<my_I5> is not the current instantiation:
                  //   (I) is not a single identifier
};

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

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
};

Имя классифицируется как член текущей инстанциации, если оно

  • неквалифицированное имя, которое найдено с помощью неквалифицированного поиска в текущей инстанциации или в её независимом базовом классе.
  • квалифицированное имя, если квалификатор (имя слева от ::) называет текущую инстанциацию, и поиск находит имя в текущей инстанциации или в её независимом базовом классе
  • имя, используемое в выражении доступа к члену класса (y в x.y или xp->y), где выражение объекта (x или *xp) является текущей инстанциацией, и поиск находит имя в текущей инстанциации или в её независимом базовом классе
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
}

Члены текущей инстанциации могут быть одновременно зависимыми и независимыми.

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

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

Неизвестные специализации

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

  • квалифицированное имя, если любое имя, появляющееся слева от :: является зависимым типом, который не является членом текущей инстанциации
  • квалифицированное имя, квалификатором которого является текущая инстанциация, и имя не найдено в текущей инстанциации или в любом из её независимых базовых классов, и существует зависимый базовый класс
  • имя члена в выражении доступа к члену класса (y в x.y или xp->y), если тип выражения объекта (x или *xp) является зависимым типом и не является текущей инстанциацией
  • имя члена в выражении доступа к члену класса (y в x.y или xp->y), если тип выражения объекта (x или *xp) является текущей инстанциацией, и имя не найдено в текущей инстанциации или в любом из её независимых базовых классов, и существует зависимый базовый класс
template<typename T>
struct Base {};
 
template<typename T>
struct Derived : Base<T>
{
    void f()
    {
        // Derived<T> refers to current instantiation
        // there is no 'unknown_type' in the current instantiation
        // but there is a dependent base (Base<T>)
        // Therefore, unknown_type is a member of unknown specialization
        typename Derived<T>::unknown_type z;
    }
};
 
template<>
struct Base<int> // this specialization provides it
{
    typedef int unknown_type;
};


Эта классификация позволяет обнаружить следующие ошибки на этапе определения шаблона (а не инстанциации):

  • Если в любом определении шаблона есть квалифицированное имя, в котором квалификатор ссылается на текущую инстанциацию, а имя не является членом текущей инстанциации и не является членом неизвестной специализации, программа некорректна (нет необходимости в диагностике), даже если шаблон никогда не будет инстанцирован.
template<class T>
class A
{
    typedef int type;
 
    void f()
    {
        A<T>::type i; // OK: 'type' is a member of the current instantiation
        typename A<T>::other j; // Error:
 
        // 'other' is not a member of the current instantiation
        // and it is not a member of an unknown specialization
        // because A<T> (which names the current instantiation),
        // has no dependent bases for 'other' to hide in.
    }
};
  • Если в любом определении шаблона есть выражение доступа к члену, где выражение объекта является текущей инстанциацией, но имя не является членом текущей инстанциации и не является членом неизвестной специализации, программа некорректна, даже если шаблон никогда не будет инстанцирован.

Члены неизвестной специализации всегда зависимые и ищутся и связываются на этапе инстанциации, как и все зависимые имена (см. выше)

Ключевое слово typename для разграничения зависимых имен

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

#include <iostream>
#include <vector>
 
int p = 1;
 
template<typename T>
void foo(const std::vector<T> &v)
{
    // std::vector<T>::const_iterator is a dependent name,
    typename std::vector<T>::const_iterator it = v.begin();
 
    // without 'typename', the following is parsed as multiplication
    // of the type-dependent member variable 'const_iterator'
    // and some variable 'p'. Since there is a global 'p' visible
    // at this point, this template definition compiles.
    std::vector<T>::const_iterator* p;
 
    typedef typename std::vector<T>::const_iterator iter_t;
    iter_t * p2; // iter_t is a dependent name, but it's known to be a type name
}
 
template<typename T>
struct S
{
    typedef int value_t; // member of current instantiation
 
    void f()
    {
        S<T>::value_t n{}; // S<T> is dependent, but 'typename' not needed
        std::cout << n << '\n';
    }
};
 
int main()
{
    std::vector<int> v;
    foo(v); // template instantiation fails: there is no member variable
            // called 'const_iterator' in the type std::vector<int>
    S<int>().f();
}

Ключевое слово typename может использоваться только таким образом перед квалифицированными именами (например, T::x), но имена не обязательно должны быть зависимыми.

Обычный поиск квалифицированных имён используется для идентификатора, префикс которого typename. В отличие от расширенного указателя типа, правила поиска не изменяются несмотря на квалификатор:

struct A // A has a nested variable X and a nested type struct X
{
    struct X {};
    int X;
};
 
struct B
{
    struct X {}; // B has a nested type struct X
};
 
template<class T>
void f(T t)
{
    typename T::X x;
}
 
void foo()
{
    A a;
    B b;
    f(b); // OK: instantiates f<B>, T::X refers to B::X
    f(a); // error: cannot instantiate f<A>:
          // because qualified name lookup for A::X finds the data member
}

Ключевое слово typename может использоваться даже вне шаблонов.

#include <vector>
 
int main()
{
    // Both OK (after resolving CWG 382)
    typedef typename std::vector<int>::const_iterator iter_t;
    typename std::vector<int> v;
}

В некоторых контекстах могут появляться только имена типов. В этих контекстах зависимое квалифицированное имя предполагается как имя типа, и typename не требуется:

  • Квалифицированное имя, используемое в качестве спецификатора объявления в (главном) последовательности спецификаторов :
    • простого объявления или определения функции в области именования;
    • объявления члена класса;
    • объявления параметра в объявлении члена класса (включая объявления дружественных функций), вне аргументов по умолчанию;
    • объявление параметра декларатора функции или шаблонной функции с квалифицированным именем, вне аргументов по умолчанию;
    • объявление параметра лямбда-выражения вне аргументов по умолчанию;
    • объявление параметра выражения requires;
    • тип в объявлении нетипового шаблона параметра;
  • Квалифицированное имя, которое появляется в типе, где наименьшее окружающее тип:
    • тип в выражении new, которое не заключает свой тип в скобки;
    • тип в объявлении псевдонима типа;
    • возвращаемый тип;
    • аргумент по умолчанию для параметра типа шаблона или
    • тип static_cast, dynamic_cast, const_cast или reinterpret_cast.
(с C++20)

Разрешение неоднозначностей шаблонов для зависимых имён

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

template<typename T>
struct S
{
    template<typename U>
    void foo() {}
};
 
template<typename T>
void bar()
{
    S<T> s;
    s.foo<T>();          // error: < parsed as less than operator
    s.template foo<T>(); // OK
}

Ключевое слово template может быть использовано таким образом только после операторов :: (разрешение по области видимости), -> (доступ к члену через указатель), и . (доступ к члену). Следующие примеры являются корректными:

  • T::template foo<X>();
  • s.template foo<X>();
  • this->template foo<X>();
  • typename T::template iterator<int>::value_type v;

Как и в случае с typename, префикс template разрешен, даже если имя не зависимое или использование не находится в области видимости шаблона.

Даже если имя слева от :: относится к пространству имён, разрешение неоднозначностей шаблонов разрешено:

template<typename>
struct S {};
 
::template S<void> q; // allowed, but unnecessary

Из-за специальных правил для поиска неквалифицированных имён для имён шаблонов в выражениях доступа к членам, когда не зависящее имя шаблона появляется в выражении доступа к члену (после -> или после .), разрешение неоднозначностей не требуется, если есть шаблон класса или алиаса(с C++11) с таким же именем, найденным обычным поиском в контексте выражения. Однако, если шаблон, найденный при поиске в контексте выражения, отличается от найденного в контексте класса, программа некорректна(до C++11)

template<int>
struct A { int value; };
 
template<class T>
void f(T t)
{
    t.A<0>::value; // Ordinary lookup of A finds a class template.
                   // A<0>::value names member of class A<0>
    // t.A < 0;    // Error: '<' is treated as the start of template argument list
}
(до C++23)

Отчеты об ошибках

Следующие поведенческие отчеты об ошибках были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применено к Поведение, как опубликовано Правильное поведение
CWG 206 C++98 не было определено, в какой момент семантические ограничения применяются, когда тип, используемый в не зависящем имени, является неполным в момент определения шаблона, но является полным в момент выполнения инстанциации программа некорректна
и в этом случае не требуется диагностическое сообщение
CWG 224 C++98 определение зависимых типов основано на форме имени, а не на поиске определение переработано
CWG 382 C++98 ключевое слово разрешения неоднозначностей typename разрешалось только в области видимости шаблона разрешено и вне шаблонов
CWG 468 C++98 ключевое слово разрешения неоднозначностей template разрешалось только в области видимости шаблона разрешено и вне шаблонов
CWG 502 C++98 не было определено, являются ли вложенные перечисления зависимыми зависимые, как вложенные классы
CWG 1047 C++98 выражения typeid никогда не зависели от значения зависимые от значения, если операнд зависит от типа
CWG 1160 C++98 не было определено, относится ли имя к текущей инстанциации, когда идентификатор шаблона, соответствующий основному шаблону или частной специализации, появляется в определении члена шаблона определено
CWG 1413 C++98 неинициализированный статический член данных, статический член-функция и адрес члена класса-шаблона не были указаны как зависящие от значения указаны
CWG 1471 C++98 вложенный тип независимого базового класса текущей инстанциации был зависимым он не зависимый
CWG 1850 C++98 список случаев, когда значение может измениться между контекстом определения и моментом инстанциации, был неполным полностью указан
CWG 1929 C++98 не было ясно, может ли ключевое слово разрешения неоднозначностей template следовать за ::, где имя слева от него относится к пространству имён разрешено
CWG 2100 C++98 адрес статического члена данных класса-шаблона не был указан как зависящий от значения указан
CWG 2276 C++98 тип функции, спецификация исключений которого зависит от значения, не являлся зависимым типом является
CWG 2307 C++98 параметр шаблона, не относящийся к типу, в скобках, используемый в качестве аргумента шаблона, был эквивалентен этому параметру шаблона больше не эквивалентен
CWG 2457 C++11 тип функции с параметрами пакета функций не был зависимым типом является

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/dependent_name

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API