🌟

[C++] std::enable_ifで特定条件のみテンプレート関数を有効化する

2023/12/20に公開3

本日はC++のenable_ifの使い方を学んだのでアウトプットしてみる。

std::enable_ifとは

std::enable_ifはC++でテンプレートを使用する際に条件を満たす場合にのみ関数やクラスを有効化するための機能。

例えばC++のテンプレート関数で引数が整数型の場合のみ使用できるようにしたい場合、以下のようにstd::enable_ifを使うことで制御することができる。

// 整数型だけ許可
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
func(T param)
{
  // paramの1ビット目をANDでビット演算
  std::cout << "result: " << (param & 1) << std::endl;
}

int main()
{
  func(static_cast<int>(0)); // result: 0
  func(static_cast<int>(1)); // result: 1
//  func(std::string("test")); // コンパイルエラー
  return 0;
}

std::is_integralでテンプレートパラメータの型が整数型かどうかを判定し、その結果を使いテンプレート関数の有効化を制御してくれる。整数型以外が指定されるとコンパイルエラーになる。

このenable_ifを使うようなやり方はSFINAE(Substitution Failure Is Not An Error)というC++の重要な概念を使ったテクニックらしい。
テンプレート関数funcは戻り値を書かずにenable_ifに制御を任せているが、こうすることで無効なテンプレートパラメータの場合にコンパイラが関数が存在しないものとして扱ってくれてコンパイルエラーとすることができるとのこと。

ORで複数条件指定する方法

enable_ifの第1テンプレートパラメータはOR演算を使うことで複数条件が指摘できる。
例えばintまたはshortだけという条件の場合は以下のように書ける。

template <typename T>
typename std::enable_if<std::is_same<T, int>::value || std::is_same<T, short>::value>::type
func(T param)
{
  // paramの1ビット目をANDでビット演算
  std::cout << "result: " << (param & 1) << std::endl;
}

戻り値の型を変える方法

最初のサンプルコードの例ではfuncの戻り値はvoidになるが、enable_ifの第2テンプレートパラメーターで戻り値の型を指定することもできる。
以下は戻り値をint型にした例。

template <typename T>
typename std::enable_if<std::is_integral<T>::value, int>::type
func(T param)
{
  return (param & 1);
}

std::enable_if_tを使いもっと簡易的に書く

C++14以降はstd::enable_if_tというエイリアスが登録されているのでこれを使うことで少しだけ簡易的に書くことができる。
以下のコードのように後ろに付けていた::typeを書かなくてもよくなる。

template <typename T>
typename std::enable_if_t<std::is_integral<T>::value, int>
func(T param)
{
  return (param & 1);
}

Discussion

齊藤敦志齊藤敦志

SFINAE を通じてテンプレートのメカニズムを把握するのは良い学習ルートなので間違いの指摘というわけではないということは先にお断りしますけども型の制約を表現するために使うのはひどく回りくどいものだと考えられており、 C++20 からはコンセプトと呼ばれる言語機能が導入されました。

この記事での例をコンセプトで表現すると

#include <concepts>

template <std::integral T>
int func(T param) {
    return param & 1;
}

といったように書けます。 Tstd::integral の制約を満たすものであるということが簡潔に表されます。

特に古い仕様に従う事情がないのであればコンセプトの形で表現するに越したことはないです。

meloQmeloQ

@saito_atsushiさん
conceptsという機能がC++20からあること初めて知りました。
教えていただきありがとうございます。
ということは、enable_ifはC++20以降あまり使う機会は無くなりそうですか?

齊藤敦志齊藤敦志

新しいプログラムであえて std::enable_if を選んで使うという機会は激減するでしょうが、今までに書かれたプログラムが急に消えてなくなるわけではないので実際には共存する状態が長く続くのではないかと思います。