コンテンツにスキップ

最終更新:2026-10-12

C++17 クラステンプレートのテンプレート引数推論 CTAD

導入

C++テンプレートメタプログラミングは、初心者から上級者まで、型パラメータを持つ汎用コードを書けることでC++開発者に広く知られた機能です。C++17でクラステンプレートのテンプレート引数推論(CTAD)が導入され、クラステンプレートを扱うプロセスはより直感的で柔軟になりました。

本節のすべてのコードもコミュニティで公開されます。

C++17でCTADが導入されたことには、次のような利点があります:

例えば、このような複雑なコードを

std::vector<FooBar<int, const char*>> obj{a, b, c}; 

次のように書くだけで済みます:

 std::vector obj{a, b, c};

別の例:

std::tuple<int>

このように簡潔にできます:

std::tuple t1{1};

というわけで、今日はCTADを見ていきましょう。

CTADはClass template argument deduction (CTAD)の略称で、クラステンプレートの引数推論のことです。コンパイラに推論ガイド(deduction guide)を与えると、この機能を使えるようになります。

与えない場合は、次の例のようになります:

#include <iostream>
template <typename T, typename U>
struct MyPair {
  T first{};
  U second{};
};

int main() {
  MyPair<int, int> p1{1, 2};
  MyPair p2{1, 2};
  std::cout << p1.first << ", " << p1.second << std::endl;
  std::cout << p2.first << ", " << p2.second << std::endl;
  return 0;
}

p2に対しては、次のエラーが出ます:

no viable constructor or deduction guide for deduction of template arguments of 'MyPair'

では、どう直せばよいのでしょうか?

deduction guideを追加するだけで、次のように書くだけです。

template <typename T, typename U>
MyPair(T, U) -> MyPair<T, U>;

クラステンプレートのテンプレート引数推論(CTAD)は、コンパイラがコンストラクタ引数からテンプレート引数を推論することを許すことで、クラステンプレートのインスタンス化プロセスを簡素化します。CTADが導入される前は、開発者はインスタンス化の際にテンプレート引数を明示的に指定しなければなりませんでした。しかしCTADによって、この明示的な指定は不要になり、コードはより読みやすく、保守しやすくなります。

template <typename T>
class Add{
    private:
        T first;
        T second;
    public:
        Add() = default;
        Add(T first, T second): first_{first}, second_{second} {}
        T result() const { return first + second; }
};

int main(){
    Add one(1,2);               // T被推导为int
    Add two{1.245, 3.1415};     // T被推导为double
    Add three = {0.24f, 0.34f}; // T被推导为float
}

上の例では、CTADによって型Tがコンストラクタ引数に基づいて推論され、明示的な型指定の必要性がなくなります。

非静的メンバの初期化

しかし、CTADが使えない場面が1つあります。それは非静的メンバの初期化です。例えば、Testクラスの中で外のMyPairを使うには、すべて書き切らなければなりません。

template <typename T = int, typename U = int>
struct MyPair {
  T first{};
  U second{};
};

template <typename T, typename U>
MyPair(T, U) -> MyPair<T, U>;

class Test {
  MyPair p4;   // use of class template 'MyPair' requires template arguments; argument
               // deduction not allowed in non-static class member
};

上の例では、テンプレートクラスMyPairを定義し、その中でCTADを使いました。しかし、クラスTestの中でMyPairを非静的メンバとして使おうとすると、非静的メンバ初期化という文脈のためCTADが正しく機能せず、コンパイルエラーになります。この場合、MyPairのテンプレート引数を明示的に指定しなければなりません。

非静的メンバ初期化の文脈では、CTADがテンプレート引数を推論できないことがわかります。したがって、クラステンプレートを非静的メンバとして使う場合、開発者はすべてのテンプレート引数を明示的に指定する必要があります。コードの正しさと保守性を確保するためにも、実際の開発ではこの点に特に注意してください。

関連トピック

コメント