Posted at

SFINAE で遊んでみた 2/2

More than 5 years have passed since last update.

#include <stdio.h>

#include <iostream>
#include <vector>
#include <map>
using namespace std;

// 準備中です...
int main() { return 0; }
struct Out {
Out() {}
Out(const Out& out) { puts(""); }
Out& operator|(const char* mes) { puts(mes); return *this; }
Out& operator|(int n) { printf("# %d // ", n); return *this; }
};
template<int> struct Holder { static Out out; };
#define O template<> Out Holder<__LINE__>::out = Out()
#define V(...) __VA_ARGS__ | #__VA_ARGS__

O | "前回 (http://codepad.org/Cr5vhuZD) の続きです.";

struct true_v {
enum { value = 1 };
};

struct false_v {
enum { value = 0 };
};

O | "★ぷりんたぶる?";

O | "前回はある型 T が ネストした型 iterator を持つかどうかを判定する is_stl を"
| "つくりました.";

O | "今回はもう少し複雑な判定器を作りたいと思います."
| "ある型 T のインスタンスが << 演算子を使って標準出力に出力可能か"
| "否かを調べる is_printable です.";

O | "複雑といっても基本戦略は is_stl と変わりません."
| "例によって false_v を継承したデフォルトの実装を作るところから始めます.";

template <class T, class X=void>
struct is_printable : public false_v {};

O | "また同様にヘルパテンプレートを定義します."
| "が,テンプレート引数の種類を class ではなく size_t としておきます.";

template <size_t>
struct ignore_value {
typedef void type;
};

O | "最後に printable な型に対してだけ置き換えが成功するテンプレートを定義して"
| "完成です.";

template <class T>
struct is_printable<T,
typename ignore_value<sizeof(std::cout << *(T*)0)>::type> :
public true_v {};

O | "前回と異なり,ignore_value の引数に指定された部分が型の式ではなく,"
| "値の式になっています.この式の結果は ignore_value によって"
| "捨てられますが,この式が正しく置き換えできるか否かでテンプレート置き換えの"
| "成否が決まります.";

O | "ignore_value の引数には値を指定する式を置かなければならないのですが,"
| "sizeof で囲むことによって,型,値,関数(演算子)適用など多種多様な式を"
| "値を示す式にすることができます.";

O | "sizeof 本来の意味としては,ここでは std::cout と T 型に << 演算子を"
| "適用した結果の型のサイズを返しているのですが,"
| "実際には std::cout と T 型に << 演算子を適用出来るか否かが"
| "焦点となっています.";

O | "std::cout << T() と T 型を直接 std::cout に出力していないのは,"
| "T がデフォルトコンストラクタを持っていない場合に正しく判定を行うためです."
| " NULL ポインタが間接参照されていますが,sizeof の中身の式は"
| "実際に実行されるわけではないので問題ありません.";

O | "それでは出力を確認してみましょう.";

// 印字できない型
struct UserTypeNotPrintable {};
// 印字できる型
struct UserTypePrintable {};
std::ostream& operator<<(std::ostream&, const UserTypePrintable&);

O | V(is_printable<int>::value)
| V(is_printable<char*>::value)
| V(is_printable<UserTypeNotPrintable>::value)
| V(is_printable<UserTypePrintable>::value);

O | "★マクロ化";

O | "せっかく sizeof の中にはいろんなものを置くことができるので,"
| "マクロ化して使いまわせるようにしたいと思います.";

#define DEF_EXP_VALIDATOR(name, arg1) \
template<class arg1, class X=void> \
struct name : public false_v {}; \
template<class arg1> \
struct name<arg1, typename ignore_value<sizeof(

#define END_EXP_VALIDATOR )>::type> : public true_v {};

DEF_EXP_VALIDATOR(is_referenceable, T)
**(T*)0
END_EXP_VALIDATOR

O | V(is_referenceable<int>::value)
| V(is_referenceable<char*>::value)
| V(is_referenceable<UserTypeNotPrintable>::value)
| V(is_referenceable<UserTypePrintable*>::value);

O | "二引数以上バージョンも作れます.";

#define DEF_EXP_VALIDATOR_2(name, arg1, arg2) \
template<class arg1, class arg2, class X=void> \
struct name : public false_v {}; \
template<class arg1, class arg2> \
struct name<arg1, arg2, typename ignore_value<sizeof(

DEF_EXP_VALIDATOR_2(is_convertable, T, U)
(*(int(*)(const T&))0)(*(U*)0)
END_EXP_VALIDATOR

O | V(is_convertable<int, UserTypeNotPrintable>::value)
| V(is_convertable<std::string, char*>::value);

O | "ちなみに今回のコード,失敗するコンパイラもあるようです."
| "前回と同様に codepad で投稿しようとしたらエラーになってしまいました...";