// 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; // error: no visible declaration of a7 // T::A is not a type name; multiplication of T::A by a7 B* a8; // error: no visible declarations of B and a8 // B is not a type name; multiplication of B by a8 } };— end example
typename-specifier: typename nested-name-specifier identifier typename nested-name-specifier template simple-template-id
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
template<class T> T::R f(); // OK, return type of a function declaration at global scope template<class T> void f(T::R); // ill-formed, no diagnostic required: attempt to declare // a void variable template template<class T> struct S { using Ptr = PtrTraits<T>::Ptr; // OK, in a defining-type-id T::R f(T::P p) { // OK, class scope return static_cast<T::R>(p); // OK, type-id of a static_cast } auto g() -> S<T*>::Ptr; // OK, trailing-return-type }; template<typename T> void f() { void (*pf)(T::X); // variable pf of type void* initialized with T::X void g(T::X); // error: T::X at block scope does not denote a type // (attempt to declare a void variable) }— end example
template <class T> void f(int i) { T::x * i; // expression, not the declaration of a variable i } 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
template<class T> struct A { typedef int B; B b; // OK, no typename required };— end example
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
#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'; } };
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
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 <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<Derived<int> > t; // OK: default argument uses injected-class-name as a template— end 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
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
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<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
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
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
postfix-expression ( expression-list )where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if
template<class T> struct X : B<T> { typename T::A* pa; void f(B<T>* pb) { static int i = B<T>::i; pb->j++; } };
typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // a has type double };
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;
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; static const long my_I4 = I; static const int my_I5 = (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 B<my_T1, T2, my_I4>* b6; // not the current instantiation B<my_T1, T2, my_I5>* b7; // not the current instantiation };— end 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
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
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
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
simple-type-specifier ( expression-list ) :: new new-placement new-type-id new-initializer :: new new-placement ( type-id ) new-initializer 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 sizeof unary-expression sizeof ( type-id ) sizeof ... ( identifier ) alignof ( type-id ) typeid ( expression ) typeid ( type-id ) :: delete cast-expression :: delete [ ] cast-expression throw assignment-expression noexcept ( expression )
sizeof unary-expression sizeof ( type-id ) typeid ( expression ) typeid ( type-id ) alignof ( type-id ) noexcept ( expression )
simple-type-specifier ( expression-list ) static_cast < type-id > ( expression ) const_cast < type-id > ( expression ) reinterpret_cast < type-id > ( expression ) ( type-id ) cast-expression
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
Source file "X.h":
namespace Q { struct X { }; }
Source file "G.h":
namespace Q { void g_impl(X, X); }
Module interface unit of M1:
module; #include "X.h" #include "G.h" export module M1; export template<typename T> void g(T t) { g_impl(t, Q::X{ }); // ADL in definition context finds Q::g_impl, g_impl not discarded }
Module interface unit of M2:
module; #include "X.h" export module M2; import M1; void h(Q::X x) { g(x); // OK }— end example
Module interface unit of Std:
export module Std; export template<typename Iter> void indirect_swap(Iter lhs, Iter rhs) { swap(*lhs, *rhs); // swap not found by unqualified lookup, can be found only via ADL }
Module interface unit of M:
export module M; import Std; struct S { /* ...*/ }; void swap(S&, S&); // #1 void f(S* p, S* q) { indirect_swap(p, q); // finds #1 via ADL in instantiation context }— end example
Source file "X.h":
struct X { /* ... */ }; X operator+(X, X);
Module interface unit of F:
export module F; export template<typename T> void f(T t) { t + t; }
Module interface unit of M:
module; #include "X.h" export module M; import F; void g(X x) { f(x); // OK: instantiates f from F, // operator+ is visible in instantiation context }— end example
Module interface unit of A:
export module A; export template<typename T> void f(T t) { cat(t, t); // #1 dog(t, t); // #2 }
Module interface unit of B:
export module B; import A; export template<typename T, typename U> void g(T t, U u) { f(t); }
Source file "foo.h", not an importable header:
struct foo { friend int cat(foo, foo); }; int dog(foo, foo);
Module interface unit of C1:
module; #include "foo.h" // dog not referenced, discarded export module C1; import B; export template<typename T> void h(T t) { g(foo{ }, t); }
Translation unit:
import C1; void i() { h(0); // error: dog not found at #2 }
Importable header "bar.h":
struct bar { friend int cat(bar, bar); }; int dog(bar, bar);
Module interface unit of C2:
module; #include "bar.h" // imports header unit "bar.h" export module C2; import B; export template<typename T> void j(T t) { g(bar{ }, t); }
Translation unit:
import C2; void k() { j(0); // OK, dog found in instantiation context: // visible at end of module interface unit of C2 }— end 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); // error: gcd is not visible }— end example