はじめに
C++になれている人であれば、当然のように知っていたことだと思いますので
テンプレート初心者向けな内容になっております。
私は恥ずかしながら最近になって知ったので共有しようと思いました。
特殊化 / 部分特殊化
クラス定義単位での特殊化
クラステンプレートの特殊化/部分特殊化をするときに結構以下みたいに書いてしまっていました。
template<class T>
struct Hoge
{
void a() const
{
std::cout << "A" << std::endl;
}
void b() const
{
std::cout << "B" << std::endl;
}
};
// 特殊化
template<>
struct Hoge<int>
{
void a() const
{
std::cout << "A by int" << std::endl;
}
void b() const
{
std::cout << "B" << std::endl;
}
};
// 部分特殊化
template<class T>
struct Hoge<T*>
{
void a() const
{
std::cout << "A by pointer" << std::endl;
}
void b() const
{
std::cout << "B" << std::endl;
}
};
クラス定義をまるごと再定義するやり方だと、
テンプレート引数ごとに実装が変わらないメンバ関数なども再定義しなくてはいけません。
上の例でいうとb()
の実装をなんども書いてしまっています。
メンバ関数単位での特殊化
で、最近知ったのですが、そもそもメンバ関数単位での特殊化もできました。
⚠️ただし、部分特殊化はできません。
template<class T>
struct Hoge
{
void a() const
{
std::cout << "A" << std::endl;
}
void b() const
{
std::cout << "B" << std::endl;
}
};
// 特殊化
template<>
void Hoge<int>::a() const
{
std::cout << "A by int" << std::endl;
}
// 部分特殊化 エラー
//template<class T>
//void Hoge<T*>::a() const
//{
// std::cout << "A by pointer" << std::endl;
//}
一部のメンバに対して部分特殊化したい場合は、関数オブジェクトに落とし込むなどの方法で対処できます。
template<class T>
struct Hoge
{
void a() const
{
AImpl<T>{}.a();
}
void b() const
{
std::cout << "B" << std::endl;
}
private:
template<class U>
struct AImpl
{
void operator()()const
{
std::cout << "A" << std::endl;
}
};
template<std::same_as<int> U>
struct AImpl<U>
{
void operator()()const
{
std::cout << "A by int" << std::endl;
}
};
template<class U>
struct AImpl<U*>
{
void operator()()const
{
std::cout << "A by pointer" << std::endl;
}
};
};
余談
少し話がそれますが、コンセプトが使用できないC++17以前では
クラススコープだとテンプレートが完全特殊化できない制限によって以下のように無理やり部分特殊化にするためにダミーのパラメータを使ったりしていました。
template<class U, class Dummy = void>
struct AImpl
{
void operator()()const
{
std::cout << "A" << std::endl;
}
};
template<class Dummy>
struct AImpl<int, Dummy>
{
void operator()()const
{
std::cout << "A by int" << std::endl;
}
};
template<class U, class Dummy>
struct AImpl<U*, Dummy>
{
void operator()()const
{
std::cout << "A by pointer" << std::endl;
}
};
C++20からは、コンセプトがあるのでsame_as
を利用することでほぼ特殊化のようなことを、ダミーパラメータなしで実現できることに気が付いた。
インナークラス単位での特殊化
インナークラスも、メンバ同様に特殊化できる
template<class T>
struct Hoge
{
void a() const
{
AImpl{}();
}
void b() const
{
std::cout << "B" << std::endl;
}
private:
struct AImpl
{
void operator()()const
{
std::cout << "A" << std::endl;
}
};
};
template<>
struct Hoge<int>::AImpl
{
void operator()()const
{
std::cout << "A by int" << std::endl;
}
};
// 部分特殊化はできない エラー
//template<class T>
//struct Hoge<T*>::AImpl
//{
// void operator()()const
// {
// std::cout << "A by pointer" << std::endl;
// }
//};
以下みたいなインナークラスのメンバ指定での定義も可能です
template<>
void Hoge<int>::AImpl::operator()()const
{
std::cout << "A by int" << std::endl;
};
部分特殊化に関しては、やはりメンバ関数のときのような対処が必要そうです。
まとめ
クラステンプレートの特定のインナークラスやメンバ関数のみに対して特殊化することができた。
- ただしその場合は、部分特殊化はできないので、回避策が必要