Help us understand the problem. What is going on with this article?

std::enable_ifを使ってオーバーロードする時、enablerを使う?

More than 5 years have passed since last update.

本の虫:C++0xにおけるenable_ifの新しい使い方

今回のネタ元は言わずと知れたC++erの江添氏のブログからですが、テンプレート引数の型の特性によってオーバーロードする方法として、enablerという変数を使う方法が紹介されています。
実際、以前の記事でもこんな感じで活用しています。

c++
extern void* enabler;
template<bool condition, typename T = void>
using enable_if_type = typename std::enable_if<condition, T>::type;

// 符号なし整数型を受け取るオーバーロード
template<typename T, enable_if_type<std::is_unsigned<T>{}>*& = enabler>
inline constexpr int ntz(T val) noexcept;

// 符号付き整数型を受け取るオーバーロード
template<typename T, enable_if_type<std::is_signed<T>{}>*& = enabler>
inline constexpr int ntz(T val) noexcept;

// 列挙型を受け取るオーバーロード
template<typename T, enable_if_type<std::is_enum<T>{}>*& = enabler>
inline constexpr int ntz(T val) noexcept;

さて、上記のブログでenablerを受け取る型をintなどではなくvoid*&にしなければならない理由が説明されています。

しかし、なぜvoidへのポインターへのリファレンスなのか。以下の形ではだめなのか。

std::enable_if<条件, int>::type = 0

これは動くが、ユーザーが誤ってテンプレート実引数を明示的に渡してしまうかもしれないというおそれがある。その点、void &&という型は、まず現実に使われないので、安全である。およそ、void *&が一体どういう型であるかを理解できるようなユーザーであれば、このenable_ifのテクニックも理解できるほどのC++上級者なので、やはり間違えることはない。

途中void &&になっていますがvoid *&のことだと思います。
要するに、こういうことでしょう。

c++
template<typename T, enable_if_type<std::is_unsigned<T>{}, int> = 0>
inline constexpr int ntz(T val) noexcept;

ntz(10u); //(1)
ntz<unsigned int, 1>(10u); //(2)

//(1)と(2)は別の実体化

まあ、この例であれば、別の実体化をされたところで結果は変わらないのですが、確かにこれはちょっといただけません。場合によっては(関数内部でstatic変数が定義されている場合など)動作もおかしなことになる可能性があります。
ですが、void*&だってenabler以外の何かが渡される可能性は0ではないのです。

c++
extern void* enabler;
extern void* other_enabler;
template<typename T, enable_if_type<std::is_unsigned<T>{}>*& = enabler>
inline constexpr int ntz(T val) noexcept;

ntz(10u); //(1)
ntz<unsigned int, other_enabler>(10u); //(2)

//(1)と(2)は別の実体化

もちろん江添氏が言及している通り、間違える可能性はほぼないとは思いますが。

調べていたら、こんな意見も見つけました
野良C++erの雑記帳: C++0x の SFINAE で気づいたこと
void*&ではなくvoid*を使えばいいじゃない、という記事です。
確かにこれでも問題なさそうに見えるのですが、しかしよく考えたらまだ少しだけ危険です。

c++
template<typename T, enable_if_type<std::is_unsigned<T>{}>* = nullptr>
inline constexpr int ntz(T val) noexcept;

ntz(10u); //(1)
ntz<unsigned int, (void*)1>(10u); //(2)

//(1)と(2)は別の実体化

明示的なテンプレート実引数を渡されてしまう危険性は、完全には排除できていません。
もっとも、ここまでして明示的なテンプレート実引数を渡そうとする人もいないとは思いますが……。

でもよく考えたらこれって、別の値が渡せる型を使っているからそういう事を考えなければならないのです。
つまり、その型の表せる値が一種類であれば、問題はありません。
そしておあつらえ向きに、C++11からコア言語入りした、「値が一種類しかない型」というものが存在します。
要するに、std::nullptr_tを使えばいいじゃない、ということです。

c++
template<typename T, enable_if_type<std::is_unsigned<T>{}, std::nullptr_t> = nullptr>
inline constexpr int ntz(T val) noexcept;

ntz(10u); //(1)
ntz<unsigned int, nullptr>(10u); //(2)

constexpr std::nullptr_t x = nullptr;
ntz<unsigned int, x>(10u); //(3)

//(1)と(2)と(3)は同じ実体化

std::nullptr_tに渡せる値はどうあがいてもnullptrだけなので、例えテンプレート実引数を明示的に渡されても、実体化されたテンプレートは一つだけになります。
こうすればそもそもenablerを用意する必要もありません。

もともと2011年の記事なので、もしかしたらこれって今更感あふれるネタだったりするのかな、とか思いながら書いてみましたが、最近のboost界隈ではどういった設計が主流なのでしょうか。
これはいけないとか、こうするといいとか、何か意見がありましたらぜひ聞かせてください。

ドワンゴは本物のプログラマを募集しているらしいです。

kazatsuyu
だいたいC++er。constexpr好き。最新規格とか追いかけるの好き。人類ははやくSFINAEを捨ててconstraintに移行すべき。 Rustもやっている。他:Haskell, TypeScriptなど
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした