2

序章

SFINAEについて読み、勉強し始めたばかりです。理解を深めるために、自分でやってみ始めました。

そのため、SFINAEの強力なトリックを使用するための便利でありながら簡単な方法について考えていました。そして、特定のタイプを占めるバイト数を計算する一連の関数について考えるのをやめました。単純な型を扱っている限り、解決策は簡単です。

template <typename T> size_t SizeOf(const T &t)
{
    return sizeof(T);
};

この単純な近似では、任意のサイズが得られます。1はchar、場合によっては4 int、できれば4は、パディングバイトまたはパディングバイトを含むchar[4]ものです。しかし、このタイプで管理される動的メモリについてはどうでしょうか。class PrettyAwesomestruct 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;
    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
  1. 2、3、4の出力の行のコメントを外すと、コンパイラは「あいまいな呼び出し」エラーを表示します。is_pointer出力2は特殊化を使用し、出力3と4は特殊化を使用すると本当に思っていましたis_arrayまあ、私は間違っていましたが、理由はわかりません。

  2. コンテナの合計サイズを取得する方法がよくありません。すべてのアイテムを繰り返してSizeOf各アイテムを呼び出すのは良い選択だと思いますが、すべてのコンテナに対してではありません。std::basic_string実行sizeof(container) + sizeof(container::value_type) * container.size()する方が速いのですが、実現できません。に特化する方法basic_string

  3. 検出クラス(反復可能とペアを検出するクラスなど)について話すと、SFINAEに関するいくつかのブログ記事とWebの例で、通常はととして定義されるaとsを作成する一般的な方法であることわかりtrue_typeました。しかし、一部の作成者はandasとを使用していることがわかりました。誰もがどちらがベストプラクティスであるか、最も標準的なものであるかを知っていますか?false_type typedefcharchar[2]charlongtrue_typefalse_type

「このライブラリ」や「このツール」を試さない理由などの答えを探しているわけではないことに注意してください。私の目標はSFINAEの実践と理解であり、手がかりやアドバイスは大歓迎です。

4

3 に答える 3

2

1.C++11のPODの概念について読む必要があります。PODタイプの要素の配列またはPODタイプの要素へのポインタはPODタイプです http://en.cppreference.com/w/cpp/concept/PODType

たとえば、次のコードは適切にコンパイルされますhttp://liveworkspace.org/code/81627f5acb546c1fb73a69c45f7cf8ec

2.このようなものはあなたを助けることができます

template<typename T>
struct is_string
{
   enum
   {
      value = false
   };
};

template<typename Char, typename Traits, typename Alloc>
struct is_string<std::basic_string<Char, Traits, Alloc>>
{
   enum
   {
      value = true
   };
};

関数

// Iterable class specialization.
template <typename T> typename std::enable_if<is_iterable<T>::value && !is_string<T>::value, size_t>::type SizeOf(const T &aValue)
{
    size_t Result = sizeof(aValue);

    for (typename T::const_iterator I = aValue.begin(); I != aValue.end(); ++I)
    {
        Result += SizeOf(*I);
    }

    return Result;
}

template <typename T> typename std::enable_if<is_string<T>::value, size_t>::type SizeOf(const T& aValue)
{
   return sizeof(aValue) + sizeof(typename T::value_type) * aValue.length();
}

3.標準には情報がsizeof(long)ありません。これは、と等しくなることはありませんがsizeof(char)sizeof(char)と等しくなることはできないsizeof(char[2])ため、2番目のバリアントが望ましいと思います。

于 2012-09-20T06:50:36.697 に答える
2

質問3に関しては、C ++ 11では、やなどの積分定数を取得するdecltype代わりに使用する方がはるかにクリーン(かつ明確)だと思います。sizeofstd::true_typestd::false_type

たとえば、あなたのis_iterable

#include <type_traits> // std::true_type, std::false_type

// Iterable class detector
template <typename T> class is_iterable {
  template <typename U> static std::true_type test(typename U::const_iterator *);
  template <typename U> static std::false_type test(...);

public:
   // Using decltype in separate typedef because of gcc 4.6 bug:
   // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709
   typedef decltype(test<T>(0)) result_type;
   static const bool value = result_type::value;
};
于 2012-09-20T07:35:11.637 に答える
1
  1. ポインタなどの「専門分野」は、実際には専門分野ではありません。それらは過負荷です。

  2. コンパイラーは最初にオーバーロード解決を実行し、次に特殊化をチェックします。正式には、「あいまいな専門分野」のようなものはありません。専門分野がないという理由だけで、ケース2、3、および4はすでに過負荷解決に失敗しています。

  3. 過負荷の解決は、引数のタイプでのみ決定されます。オーバーロードはリターンタイプのみが異なります。確かに一部のオーバーロードは無効になっている可能性がありますが、1つを除くすべてのオーバーロードを無効にする必要があります。現在、PODアレイは、PODとアレイの両方のオーバーロードを有効にします。

  4. コンテナの場合、おそらくより良い解決策はを使用することContainer.size()です。

  5. char[2]sizeof(long)標準によれば、1にすることができるので、が好ましい。

とにかく答える質問の1つは、「配列のオーバーロードをどのように記述すればよいか」です。配列への参照があるトリックは次のとおりです。

template<typename T, unsigned N> 
constexpr size_t SizeOf(const T (&aValue)[N])
{
  // return N * sizeof(T); If you want to do the work yourself
  return sizeof(aValue); // But why bother?
}
于 2012-09-20T08:43:06.230 に答える