序章
SFINAEについて読み、勉強し始めたばかりです。理解を深めるために、自分でやってみ始めました。
そのため、SFINAEの強力なトリックを使用するための便利でありながら簡単な方法について考えていました。そして、特定のタイプを占めるバイト数を計算する一連の関数について考えるのをやめました。単純な型を扱っている限り、解決策は簡単です。
template <typename T> size_t SizeOf(const T &t)
{
return sizeof(T);
};
この単純な近似では、任意のサイズが得られます。1はchar
、場合によっては4 int
、できれば4は、パディングバイトまたはパディングバイトを含むchar[4]
ものです。しかし、このタイプで管理される動的メモリについてはどうでしょうか。class PrettyAwesome
struct AmazingStuff
したがって、指定されたタイプがポインタタイプであるかどうかを確認すると、合計サイズはポインタのサイズにポイントされたメモリのサイズ(存在する場合)を加えたものになります。
template <typename T> size_t SizeOf(const T &*t)
{
size_t Result = sizeof(t);
if (t)
{
Result += sizeof(T);
}
return Result;
};
はい、現時点ではSFINAEはまったく必要ないようですが、コンテナについて考えてみましょう。SizeOf
コンテナは、その各要素のサイズの合計に加えたものである必要があります。sizeof(container_type)
これは、SFINAEが入力する場所です。
template <typename T> size_t SizeOf(const T &t)
{
size_t Result = sizeof(t);
for (T::const_iterator i = t.begin(); i != t.end(); ++i)
{
Result += SizeOf(*i);
}
return Result;
};
上記のコードで、tyeT
タイプにconst_iterator
が必要かどうかを検出します。コンテナがマップである場合は、ペアの特殊化も必要です。
質問
最後に、質問はここから始まります。私は何を試し、どのような問題で立ち往生していますか?
#include <type_traits>
#include <string>
#include <map>
#include <iostream>
#include <vector>
// Iterable class detector
template <typename T> class is_iterable
{
template <typename U> static char has_iterator(typename U::const_iterator *);
template <typename U> static long has_iterator(...);
public:
enum
{
value = (sizeof(has_iterator<T>(0)) == sizeof(char))
};
};
// Pair class detector
template <typename T> class is_pair
{
template <typename U> static char has_first(typename U::first_type *);
template <typename U> static long has_first(...);
template <typename U> static char has_second(typename U::second_type *);
template <typename U> static long has_second(...);
public:
enum
{
value = (sizeof(has_first<T>(0)) == sizeof(char)) && (sizeof(has_second<T>(0)) == sizeof(char))
};
};
// Pointer specialization.
template <typename T> typename std::enable_if<std::is_pointer<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
if (aValue)
{
Result += sizeof(T);
}
return Result;
}
// Iterable class specialization.
template <typename T> typename std::enable_if<is_iterable<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
for (T::const_iterator I = aValue.begin(); I != aValue.end(); ++I)
{
Result += SizeOf(*I);
}
return Result;
}
// Pair specialization.
template <typename T> typename std::enable_if<is_pair<T>::value, size_t>::type SizeOf(const T &aValue)
{
return SizeOf(aValue.first) + SizeOf(aValue.second);
}
// Array specialization.
template <typename T> typename std::enable_if<std::is_array<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
for (T *I = std::begin(aValue); I != std::end(aValue); ++I)
{
SizeOf(*I);
}
return Result;
}
// Other types.
template <typename T> typename std::enable_if<std::is_pod<T>::value, size_t>::type SizeOf(const T &aValue)
{
return sizeof(aValue);
}
int main(int argc, char **argv)
{
int Int;
int *IntPtr = ∬
int twoints[2] = {0, 0};
int *twointpointers[2] = {IntPtr};
std::string SO("StackOverflow");
std::wstring WSO(L"StackOverflow");
std::map<std::string, char> m;
std::vector<float> vf;
m[SO] = 'a';
std::cout << "1: " << SizeOf(Int) << '\n';
// std::cout << "2: " << SizeOf(IntPtr) << '\n';
// std::cout << "3: " << SizeOf(twoints) << '\n';
// std::cout << "4: " << SizeOf(twointpointers) << '\n';
std::cout << "5: " << SizeOf(SO) << '\n';
std::cout << "6: " << SizeOf(WSO) << '\n';
std::cout << "7: " << SizeOf(m) << '\n';
std::cout << "8: " << SizeOf(vf) << '\n';
return 0;
}
上記のコードはこの出力を生成します:
1: 4
5: 45
6: 58
7: 66
8: 20
2、3、4の出力の行のコメントを外すと、コンパイラは「あいまいな呼び出し」エラーを表示します。
is_pointer
出力2は特殊化を使用し、出力3と4は特殊化を使用すると本当に思っていましたis_array
。まあ、私は間違っていましたが、理由はわかりません。コンテナの合計サイズを取得する方法がよくありません。すべてのアイテムを繰り返して
SizeOf
各アイテムを呼び出すのは良い選択だと思いますが、すべてのコンテナに対してではありません。std::basic_string
実行sizeof(container) + sizeof(container::value_type) * container.size()
する方が速いのですが、実現できません。に特化する方法basic_string
。検出クラス(反復可能とペアを検出するクラスなど)について話すと、SFINAEに関するいくつかのブログ記事とWebの例で、通常はととして定義されるaとsを作成する一般的な方法であることがわかり
true_type
ました。しかし、一部の作成者はandasとを使用していることがわかりました。誰もがどちらがベストプラクティスであるか、最も標準的なものであるかを知っていますか?false_type
typedef
char
char[2]
char
long
true_type
false_type
「このライブラリ」や「このツール」を試さない理由などの答えを探しているわけではないことに注意してください。私の目標はSFINAEの実践と理解であり、手がかりやアドバイスは大歓迎です。