Namespaces
Variants

Dependent names

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

テンプレートの定義内( template class template function template の両方)では、一部の構成要素の意味がインスタンス化ごとに異なる場合があります。特に、型や式は型テンプレートパラメータの型や定数テンプレートパラメータの値に依存する可能性があります。

template<typename T>
struct X : B<T> // 「B<T>」はTに依存する
{
    typename T::A* pa; // 「T::A」はTに依存する
                       // (この「typename」の使用法の意味については後述)
    void f(B<T>* pb)
    {
        static int i = B<T>::i; // 「B<T>::i」はTに依存する
        pb->j++; // 「pb->j」はTに依存する
    }
};

名前 lookup とバインディングは、依存名と非依存名で異なります。

目次

注記: - 「Contents」を「目次」に翻訳しました - C++専門用語(Binding rules、Lookup rules、Dependent types、typename、templateなど)は原文のまま保持しています - HTMLタグ、属性、構造は完全に保持されています - 番号付けとフォーマットは変更していません

バインディング規則

非依存名はテンプレート定義時点で検索され束縛されます。この束縛は、たとえテンプレート実体化時点でより適切なマッチがあった場合でも保持されます:

#include <iostream>
void g(double) { std::cout << "g(double)\n"; }
template<class T>
struct S
{
    void f() const
    {
        g(1); // 「g」は非依存名、ここで解決される
    }
};
void g(int) { std::cout << "g(int)\n"; }
int main()
{
    g(1);  // g(int)を呼び出す
    S<int> s;
    s.f(); // g(double)を呼び出す
}

非依存名の意味がテンプレートの定義コンテキストと特殊化のインスタンス化地点の間で変化する場合、プログラムは不適格となり、診断は不要です。これは以下の状況で発生する可能性があります:

  • 非依存名で使用される型は、 incomplete 型が定義時点では不完全であるが、インスタンス化時点では完全である場合
  • テンプレート定義内での名前の検索が using-declaration を見つけたが、インスタンス化における対応するスコープでの検索では宣言を一切見つけられない。これはusing-declarationがパック展開であり、対応するパックが空であるため
(C++17以降)
  • インスタンス化が、定義時点では定義されていなかったデフォルト引数またはデフォルトテンプレート引数を使用する場合
  • インスタンス化時点での 定数式 が、整数型またはスコープなし列挙型のconstオブジェクトの値 、constexprオブジェクトの値、参照の値、またはconstexpr関数の定義 (C++11以降) を使用し、かつそのオブジェクト /参照/関数 (C++11以降) が定義時点では定義されていなかった場合
  • テンプレートがインスタンス化時点で非依存クラステンプレート特殊化 または変数テンプレート特殊化 (C++14以降) を使用し、かつ使用されているこのテンプレートが、定義時点では定義されていなかった部分特殊化からインスタンス化されたもの、または定義時点では宣言されていなかった明示的特殊化を指定している場合

依存名のバインディングは、ルックアップが行われるまで延期されます。

ルックアップ規則

テンプレートで使用される依存名の lookup は、テンプレート引数が判明するまで延期され、その時点で

  • non-ADL lookupは、テンプレート定義コンテキストから可視な外部リンケージを持つ関数宣言を検査します
  • ADL は、テンプレート定義コンテキストまたはテンプレート実体化コンテキストのいずれかから可視な外部リンケージを持つ関数宣言を検査します

(言い換えれば、テンプレート定義後に新しい関数宣言を追加しても、ADLを除いて可視にはなりません)。

このルールの目的は、テンプレートのインスタンス化における ODR 違反を防ぐためのものです:

// 外部ライブラリ
namespace E
{
    template<typename T>
    void writeObject(const T& t)
    {
        std::cout << "Value = " << t << '\n';
    }
}
// 翻訳単位1:
// プログラマ1は E::writeObject が 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); // エラー: P1::operator<< を見つけられない
    }
}
// 翻訳単位2:
// プログラマ2は E::writeObject が 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); // エラー: P2::operator<< を見つけられない
    }
}

上記の例では、インスタンス化コンテキストからの非ADLルックアップが operator<< に対して許可されていた場合、 E :: writeObject < vector < int >> のインスタンス化は二つの異なる定義を持つことになります:一つは P1 :: operator << を使用し、もう一つは P2 :: operator << を使用します。このようなODR違反はリンカーによって検出されない可能性があり、両方のインスタンスでいずれか一方が使用される結果を招きます。

ADLにユーザー定義の名前空間を検査させるには、 std::vector をユーザー定義のクラスに置き換えるか、その要素型がユーザー定義のクラスである必要があります:

namespace P1
{
    // CがP1名前空間で定義されたクラスの場合
    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: writeObject(std::vector<P1::C>)をインスタンス化
                           //     ADL経由でP1::operator<<を発見
    }
}

注意: このルールにより、標準ライブラリ型に対する演算子のオーバーロードは非現実的になります:

#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
// 悪い例: 演算子はグローバル名前空間にあるが、引数は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: 通常のルックアップで::operator<<が見つかる
    std::copy(v.begin(), v.end(),
              std::ostream_iterator<elem_t>(std::cout, " "));
    // エラー: std::ostream_iteratorの定義ポイントからの通常のルックアップと
    // ADLはstd名前空間のみを考慮し、std::operator<<の多くのオーバーロードを
    // 見つけるため、ルックアップは実行される。
    // その後、オーバーロード解決は、ルックアップで見つかったセット内で
    // elem_tに対するoperator<<を見つけることができない。
}

注: 依存名(従属名)の限定された検索(ただし束縛ではない)は、テンプレート定義時にも必要に応じて行われ、非依存名と区別するため、またそれらが現在のインスタンス化のメンバーか未知の特殊化のメンバーかを判断するために行われます。この検索によって得られた情報はエラーの検出に使用できます。詳細は後述を参照してください。

依存型

以下の型は dependent types です:

  • テンプレートパラメータ
  • 未知の特殊化のメンバー(下記参照)
  • 未知の特殊化の依存メンバーであるネストされたクラス/列挙型(下記参照)
  • 依存型のCV修飾版
  • 依存型から構築された複合型
  • 要素型が依存型である、または境界(存在する場合)が値依存である配列型
(C++11以降)
  • 例外仕様が値に依存する関数型
  • template-id で、以下のいずれかの条件を満たすもの
  • テンプレート名がテンプレートパラメータである場合、または
  • テンプレート引数のいずれかが型依存、または値依存である場合 、またはパック展開である場合 (C++11以降) (たとえテンプレートIDがその引数リストなしで使用される場合でも、 injected-class-name として)
  • 型依存の式に適用された decltype の結果

型依存の式に適用された decltype の結果は、一意の依存型です。2つのそのような結果が同じ型を参照するのは、それらの式が 等価 である場合のみです。

(C++11以降)

型依存の定数式に適用されたpack indexing specifierは、一意の依存型です。2つのそのようなpack indexing specifierが同じ型を参照するのは、それらの定数式が等価である場合のみです。それ以外の場合、2つのそのようなpack indexing specifierが同じ型を参照するのは、それらのインデックスが同じ値を持つ場合のみです。

(C++26以降)

注: 現在のインスタンス化のtypedefメンバは、それが参照する型が依存型である場合にのみ依存型となります。

型依存式

以下の式は type-dependent です:

  • 任意の部分式が型依存式である式
  • this (クラスが依存型の場合)
  • 識別子式 で、 concept-id ではなく、 (C++20以降) であるもの
  • 名前探索によって少なくとも1つの依存宣言が見つかる識別子を含む
  • 依存 template-id を含む
  • 特殊識別子 __func__ を含む (何らかの外側の関数がテンプレート、クラステンプレートの非テンプレートメンバー 、またはジェネリックラムダ (C++14以降) である場合)
(C++11以降)
  • 依存型への 変換関数 の名前を含む
  • 未知の特殊化のメンバーであるネストされた名前指定子または 修飾ID を含む
  • 現在のインスタンス化の依存メンバーであり、「未知の境界を持つ配列」型の静的データメンバーを指す
  • 名前探索が、現在のインスタンス化のメンバ関数の宣言を1つ以上見つける識別子を含み、それらが 戻り値型推論 を用いて宣言されている場合
(C++14以降)
  • 名前探索が、初期化子が型依存である 構造化束縛宣言 を見つける識別子を含む場合
  • 名前探索が、型にプレースホルダー auto を含む定数テンプレートパラメータを見つける識別子を含む場合
  • 名前探索が、プレースホルダー型を含む型で宣言された変数(例: auto 静的データメンバ)を見つける識別子を含み、初期化子が型依存である場合
(C++17以降)
  • 名前探索が パック を見つける識別子を含む場合
(C++26以降)
  • 依存型への任意のキャスト式
  • new で依存型のオブジェクトを作成する場合
  • 現在のインスタンス化のメンバーで、その型が依存型であるメンバーアクセス式
  • 未知の特殊化のメンバーを参照するメンバーアクセス式
(C++17以降)
(since C++26)
**日本語訳:**
(C++26以降)

以下の式は、これらの式の型が型依存になりえないため、決して型依存にはなりません:

(C++11以降)
(注:指示に従い、HTMLタグ・属性、 タグ内のC++キーワードはすべて原文のまま保持しています。翻訳対象となるテキストが存在しないため、出力は入力と同一になります)
(C++20以降)

値依存式

以下の式は value-dependent です:

  • 定数式が要求される文脈で使用され、その任意の部分式が値に依存する式
  • 以下のいずれかの条件を満たす 識別子式
  • これは concept-id であり、その引数のいずれかが依存型である場合。
(C++20以降)
  • 型依存である。
  • 定数テンプレートパラメータの名前である。
  • 現在のインスタンス化の依存メンバーであり、初期化されていない静的データメンバーの名前である。
  • 現在のインスタンス化の依存メンバーである静的メンバー関数の名前である。
  • 整数または列挙型 (C++11まで) リテラル型 (C++11以降) の定数であり、値依存式から初期化される。
  • オペランドが型依存式である以下の式:
(C++11以降)
  • オペランドが依存型IDである以下の式:
  • 対象の型が依存型であるか、オペランドが型依存式である以下の式:
  • function-style cast 対象型が依存型であるか、値依存式が括弧で囲まれた関数形式キャスト式 または波括弧 (C++11以降)
(C++11以降)
(C++17以降)
  • 引数が現在のインスタンス化の依存メンバーを名前付けする 修飾識別子 であるアドレス取得式
  • 引数がコア 定数式 として評価された場合、静的 またはスレッド記憶域期間 (C++11以降) を持つオブジェクト、またはメンバー関数である テンプレート化されたエンティティ を参照する任意の式であるアドレス取得式

依存名

現在のインスタンス化

クラステンプレート定義内(そのメンバ関数およびネストされたクラスを含む)では、一部の名前は 現在のインスタンス化 を参照するものと推論されることがあります。これにより、特定のエラーをインスタンス化時ではなく定義時点で検出できるようになり、依存名に対する typename および template ディスアンビギュエータの要件が削除されます(後述参照)。

現在のインスタンスを参照できる名前は以下のみです:

  • クラステンプレートの定義、クラステンプレートのネストされたクラス、クラステンプレートのメンバ、またはクラステンプレートのネストされたクラスのメンバ内で:
    • クラステンプレートまたはネストされたクラスの注入クラス名
  • プライマリクラステンプレートまたはプライマリクラステンプレートのメンバの定義内で:
    • クラステンプレートの名前にテンプレート引数リスト(または同等のエイリアステンプレートの特殊化)が続き、各引数が対応するパラメータと等価(以下で定義)である場合
  • クラステンプレートのネストされたクラスの定義内で:
    • 現在のインスタンス化のメンバとして使用されるネストされたクラスの名前
  • クラステンプレートの部分特殊化またはクラステンプレートの部分特殊化のメンバの定義内で:
    • クラステンプレートの名前に部分特殊化のテンプレート引数リストが続き、各引数が対応するパラメータと等価である場合
  • テンプレート化された関数 の定義内で:

テンプレート引数は、以下の場合にテンプレートパラメータと等価です

  • for a type parameter , the template argument denotes the same type as the template parameter.
  • for a constant parameter , the template argument is an identifier that names a variable that is equivalent to the template parameter. A variable is equivalent to a template parameter if
  • テンプレートパラメータと同じ型(cv修飾子を無視)を持ち、かつ
  • その初期化子が単一の識別子から構成され、その識別子がテンプレートパラメータを参照するか、再帰的にそのような変数を参照する場合。
template<class T>
class A
{
    A* p1;      // Aは現在のインスタンス化
    A<T>* p2;   // A<T>は現在のインスタンス化
    ::A<T>* p4; // ::A<T>は現在のインスタンス化
    A<T*> p3;   // A<T*>は現在のインスタンス化ではない
    class B
    {
        B* p1;                 // Bは現在のインスタンス化
        A<T>::B* p2;           // A<T>::Bは現在のインスタンス化
        typename A<T*>::B* p3; // A<T*>::Bは現在のインスタンス化ではない
    };
};
template<class T>
class A<T*>
{
    A<T*>* p1; // A<T*>は現在のインスタンス化
    A<T>* p2;  // A<T>は現在のインスタンス化ではない
};
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>は現在のインスタンス化:
                  //   my_IはIと同じ型を持ち、
                  //   単にIで初期化されている
    B<my_I2>* b2; // B<my_I2>は現在のインスタンス化ではない:
                  //   I + 0は単一の識別子ではない
    B<my_I3>* b3; // B<my_I3>は現在のインスタンス化:
                  //   my_I3はIと同じ型を持ち、
                  //   単にmy_I(Iと等価)で初期化されている
    B<my_I4>* b4; // B<my_I4>は現在のインスタンス化ではない:
                  //   my_I4の型(long)はIの型(int)と同じではない
    B<my_I5>* b5; // B<my_I5>は現在のインスタンス化ではない:
                  //   (I)は単一の識別子ではない
};

基底クラスは、ネストされたクラスがその外側のクラステンプレートから派生している場合、現在のインスタンス化となり得ることに注意してください。依存型ではあるが現在のインスタンス化ではない基底クラスは 依存基底クラス となります:

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

名前は、以下の場合に現在のインスタンス化のメンバーとして分類されます

  • 現在のインスタンス化またはその非依存基底クラスにおいて 非修飾名探索 によって発見された非修飾名
  • 修飾名 (修飾子( :: の左側の名前)が現在のインスタンス化を指し、探索が現在のインスタンス化またはその非依存基底クラスで名前を発見する場合)
  • クラスメンバアクセス式で使用される名前( x. y または xp - > y y )。オブジェクト式( x または * xp )が現在のインスタンス化であり、探索が現在のインスタンス化またはその非依存基底クラスで名前を発見する場合
template<class T>
class A
{
    static const int i = 5;
    int n1[i];       // i は現在のインスタンス化のメンバーを参照
    int n2[A::i];    // A::i は現在のインスタンス化のメンバーを参照
    int n3[A<T>::i]; // A<T>::i は現在のインスタンス化のメンバーを参照
    int f();
};
template<class T>
int A<T>::f()
{
    return i; // i は現在のインスタンス化のメンバーを参照
}

現在のインスタンス化のメンバーは、依存関係を持つ場合と持たない場合の両方があり得ます。

現在のインスタンス化のメンバーのルックアップが、インスタンス化ポイントと定義ポイントで異なる結果を与える場合、そのルックアップは曖昧となります。ただし、メンバー名が使用される際、自動的にはクラスメンバーアクセス式に変換されないことに注意してください。明示的なメンバーアクセス式のみが現在のインスタンス化のメンバーを示します:

struct A { int m; };
struct B { int m; };
template<typename T>
struct C : A, T
{
    int f() { return this->m; } // テンプレート定義コンテキストで A::m を見つける
    int g() { return m; }       // テンプレート定義コンテキストで A::m を見つける
};
template int C<B>::f(); // エラー: A::m と B::m の両方を見つける
template int C<B>::g(); // OK: クラスメンバアクセス構文への変換は
                        // テンプレート定義コンテキストでは発生しない

未知の特殊化

テンプレート定義内では、特定の名前は 不明な特殊化 に属すると見なされます。特に、

  • 修飾名 :: の左側に現れる名前が依存型であり、現在のインスタンス化のメンバーでない場合)
  • 修飾名 (修飾子が現在のインスタンス化であり、名前が現在のインスタンス化またはその非依存基底クラスで見つからず、依存基底クラスが存在する場合)
  • クラスメンバーアクセス式におけるメンバー名( x. y または xp - > y y )、オブジェクト式の型( x または * xp )が依存型であり、現在のインスタンス化でない場合
  • クラスメンバーアクセス式におけるメンバー名( x. y または xp - > y y )、オブジェクト式の型( x または * xp )が現在のインスタンス化であり、名前が現在のインスタンス化またはその非依存基底クラスで見つからず、依存基底クラスが存在する場合
template<typename T>
struct Base {};
template<typename T>
struct Derived : Base<T>
{
    void f()
    {
        // Derived<T> は現在のインスタンス化を参照
        // 現在のインスタンス化には "unknown_type" は存在しない
        // しかし依存基底クラス (Base<T>) が存在する
        // したがって、"unknown_type" は未知の特殊化のメンバーである
        typename Derived<T>::unknown_type z;
    }
};
template<>
struct Base<int> // この特殊化が unknown_type を提供する
{
    typedef int unknown_type;
};


この分類により、テンプレート定義時点(インスタンス化時ではなく)で以下のエラーを検出することが可能になります:

  • テンプレート定義内で修飾名が使用されており、その修飾子が現在のインスタンス化を参照し、かつその名前が現在のインスタンス化のメンバーでも未知の特殊化のメンバーでもない場合、そのテンプレートがインスタンス化されない場合でも、プログラムは不適格(診断不要)となります。
template<class T>
class A
{
    typedef int type;
    void f()
    {
        A<T>::type i; // OK: 「type」は現在のインスタンス化のメンバー
        typename A<T>::other j; // エラー:
        // 「other」は現在のインスタンス化のメンバーではない
        // かつ未知の特殊化のメンバーでもない
        // なぜならA<T>(現在のインスタンス化を指す)は、
        // 「other」が隠れる依存基底クラスを持たないため
    }
};
  • テンプレート定義内で、オブジェクト式が現在のインスタンス化でありながら、その名前が現在のインスタンス化のメンバーでも未知の特殊化のメンバーでもないメンバーアクセス式が存在する場合、そのテンプレートがインスタンス化されない場合でもプログラムは不適格となります。

不明な特殊化のメンバーは常に依存名であり、すべての依存名と同様にインスタンス化の時点で検索と束縛が行われる(上記参照)

(注:元のテキストには翻訳すべき自然言語のコンテンツが含まれていません。HTMLタグと属性は翻訳対象外であり、指定された条件に従って対応しています)

依存名に対する 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 は依存名であり、
    typename std::vector<T>::const_iterator it = v.begin();
    // 「typename」がない場合、以下は型依存のデータメンバ「const_iterator」と
    // ある変数「p」の乗算として解析される。この時点でグローバルな「p」が
    // 可視であるため、このテンプレート定義はコンパイルされる。
    std::vector<T>::const_iterator* p;
    typedef typename std::vector<T>::const_iterator iter_t;
    iter_t * p2; // 「iter_t」は依存名であるが、型名であることが既知
}
template<typename T>
struct S
{
    typedef int value_t; // 現在のインスタンス化のメンバ
    void f()
    {
        S<T>::value_t n{}; // S<T>は依存するが、「typename」は不要
        std::cout << n << '\n';
    }
};
int main()
{
    std::vector<int> v;
    foo(v); // テンプレートのインスタンス化に失敗: 型 std::vector<int> には
            // 「const_iterator」というメンバ変数が存在しない
    S<int>().f();
}

キーワード typename は、修飾名の前でのみこのように使用できます(例: T :: x )、ただし名前は依存名である必要はありません。

通常の 限定名探索 が、 typename で始まる識別子に対して使用されます。 詳細型指定子 の場合とは異なり、修飾子があっても探索規則は変更されません:

struct A // Aはネストされた変数Xとネストされた型struct Xを持つ
{
    struct X {};
    int X;
};
struct B
{
    struct X {}; // Bはネストされた型struct Xを持つ
};
template<class T>
void f(T t)
{
    typename T::X x;
}
void foo()
{
    A a;
    B b;
    f(b); // OK: f<B>をインスタンス化、T::XはB::Xを参照
    f(a); // エラー: f<A>をインスタンス化できません:
          // 修飾名検索でA::Xがデータメンバを見つけるため
}

キーワード typename は、テンプレートの外側でも使用できます。

#include <vector>
int main()
{
    // 両方ともOK (CWG 382解決後)
    typedef typename std::vector<int>::const_iterator iter_t;
    typename std::vector<int> v;
}

一部の文脈では、型名のみが有効に現れることができます。これらの文脈では、依存する修飾名は型を指すものと見なされ、 typename は必要ありません:

  • 修飾名が以下の(トップレベル) decl-specifier-seq 内で 宣言指定子 として使用される場合:
  • 修飾名が type-id 内に現れ、最も内側の囲むtype-idが以下の場合:
(C++20以降)

依存名に対する template 曖昧性除去子

同様に、テンプレート定義において、現在のインスタンス化のメンバーではない依存名は、明確化キーワード template が使用されない限り、またはそれが既にテンプレート名として確立されていない限り、テンプレート名とは見なされません:

template<typename T>
struct S
{
    template<typename U>
    void foo() {}
};
template<typename T>
void bar()
{
    S<T> s;
    s.foo<T>();          // エラー: < が小なり演算子として解析される
    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 ;
(注:指示通り、HTMLタグ・属性、 タグ内のC++コード、C++専門用語は翻訳せず、元のフォーマットを保持しています)

typename の場合と同様に、 template 接頭辞は、名前が依存していない場合や、テンプレートのスコープ内で使用されていない場合でも許可されます。

左側の名前が名前空間を参照している場合でも、テンプレート曖昧性除去子が許可されます:

template<typename>
struct S {};
::template S<void> q; // 許可されるが、不要

メンバアクセス式におけるテンプレート名の 非修飾名探索 に関する特別なルールにより、非依存テンプレート名がメンバアクセス式( - > の後、または . の後)に現れる場合、式のコンテキストでの通常の探索によって同じ名前の classまたはalias (C++11以降) テンプレートが見つかるときは、曖昧性除去子は不要です。 ただし、式のコンテキストでの探索によって見つかったテンプレートがクラスのコンテキストで見つかったものと異なる場合、プログラムは不適格です (C++11まで)

template<int>
struct A { int value; };
template<class T>
void f(T t)
{
    t.A<0>::value; // Aの通常の探索はクラステンプレートを見つける
                   // A<0>::value はクラスA<0>のメンバを指す
    // t.A < 0;    // エラー: 「<」はテンプレート引数リストの開始として扱われる
}
(C++23まで)

キーワード

template , typename

不具合報告

以下の動作変更の欠陥報告書は、以前に公開された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 テンプレートのメンバー定義内でプライマリテンプレートまたは部分特殊化に
マッチするテンプレートIDが現れた場合、その名前が現在のインスタンス化を
参照するかどうかは未規定であった
規定された
CWG 1413 C++98 初期化されていない静的データメンバ、静的メンバ関数、および
クラステンプレートのメンバーのアドレスは値依存としてリストされていなかった
リストに追加された
CWG 1471 C++98 現在のインスタンス化の非依存基底クラスの
ネスト型は依存型であった
依存型ではない
CWG 1850 C++98 定義コンテキストとインスタンス化ポイントの間で
意味が変化する可能性があるケースのリストが不完全であった
完全なものとなった
CWG 1929 C++98 template 識別子が名前空間を参照する
:: の後に続けることができるか明確ではなかった
許可される
CWG 2066 C++98 this は値依存ではなかった 値依存となる可能性が
ある
CWG 2100 C++98 クラステンプレートの静的データメンバーのアドレスは
値依存としてリストされていなかった
リストに追加された
CWG 2109 C++98 型依存の識別子式が値依存ではない可能性があった 常に値依存となる
CWG 2276 C++98 例外指定が値依存である関数型は
依存型ではなかった
依存型となる
CWG 2307 C++98 テンプレート引数として使用される括弧で囲まれた定数テンプレートパラメータは
そのテンプレートパラメータと等価であった
もはや等価ではない
CWG 2457 C++11 関数パラメータパックを持つ関数型は
依存型ではなかった
依存型となる
CWG 2785 C++20 requires 式が型依存となる可能性があった 型依存になることは
ない
CWG 2905 C++11 noexcept 式はオペランドが値依存の場合にのみ
値依存であった
オペランドがテンプレートパラメータを
含む場合に値依存となる
CWG 2936 C++98 テンプレート化された関数のローカルクラスの名前は
現在のインスタンス化の一部ではなかった
現在のインスタンス化の一部となる