9

C++11のenable_ifの次の例を見ました。

struct is_64_bit
{
    static const bool value = sizeof(void*) == 8;
};

enable_if<is_64_bit::value, void>::type
my_memcpy(void* target, const void* source, size_t n)
{
    cout << "64 bit memcpy" << endl;
}

enable_if<!is_64_bit::value, void>::type
my_memcpy(void* target, const void* source, size_t n)
{
    cout << "32 bit memcpy" << endl;
}

私が理解しているように、システムアーキテクチャに応じて、「my_memcpy」関数は32ビットバージョンまたは64ビットバージョンのいずれかで使用できます。しかし、コンパイル時に次のエラーが発生します。

error: ‘type’ in ‘struct std::enable_if<false, void>’ does not name a type

32バージョンのみが利用可能であると思ったので少し混乱しています(私はLinux Fedora 32ビットを使用しています)。

たぶん、この例に何か問題がありますか?または私は何かが欠けていますか?

ありがとう。

4

3 に答える 3

12

std::enable_if置換失敗はエラーではない(SFINAE)の原則に従って動作します。これは、関数テンプレートのインスタンス化で特定のタイプのエラーが発生した場合、プログラムは過負荷解決に関与しないその関数テンプレートでコンパイルを続行することを示します。

SFINAEを開始するには、(a)関数(またはメソッド)テンプレートで使用する必要があり、(b)テンプレートパラメーターに依存する必要があります。あなたのプログラムは両方の点で失敗します。

enable_ifテンプレートパラメータに依存させるための最も簡単な方法は、デフォルトのパラメータを追加することです。

template<typename T = void> typename enable_if<is_64_bit::value, T>::type
my_memcpy(void* target, const void* source, size_t n)

ただし、これは一般的に賢明な使用法ではありませんenable_if。コンパイルエラーのインターセプトに依存するため、コストがかかる傾向があります。あなたの場合、テンプレートの特殊化ははるかに良い考えです:

#include <iostream>

template<int = sizeof(void *)> void my_memcpy(void* target, const void* source, size_t n);

template<> void my_memcpy<8>(void* target, const void* source, size_t n) {
    std::cout << "64 bit memcpy" << std::endl;
}

template<> void my_memcpy<4>(void* target, const void* source, size_t n) {
    std::cout << "32 bit memcpy" << std::endl;
}
于 2012-09-27T16:24:17.507 に答える
10

テンプレートは、条件Bがである場合template< bool B, class T = void > struct enable_ifにのみaを持つように特殊化されています。typedef typetrue

お使いのコンパイラは正しいです。のtypedefはありませtypestruct std::enable_if<false, void>。にはtypedefのみがありますstruct std::enable_if<true, void>

詳細については、こちらをご覧ください。

したがって、問題を修正するには、Bがコンパイルされないenable_ifと評価されるBが含まれていることを確認する必要があります。関数テンプレートを作成することにより、 SFINAEfalseの助けを借りてこれを達成することができます。コンパイラは、Bが評価する関数テンプレートのコンパイルに失敗してもエラーを報告せず、Bが評価する関数を正常にコンパイルして使用します。my_memcpyfalsetrue

#include <iostream>
#include <type_traits>

using namespace std;


struct is_64_bit
{
   static const bool value = sizeof(void*) == 8;
};

template<typename T>
typename enable_if<is_64_bit::value, T>::type
my_memcpy(T* target, const T* source, size_t n)
{
  cout << "64 bit memcpy" << endl;
}

template<typename T>
typename enable_if<!is_64_bit::value, T>::type
my_memcpy(T* target, const T* source, size_t n)
{
  cout << "32 bit memcpy" << endl;
}
于 2012-09-27T15:53:22.490 に答える
5

SFINAEはテンプレート用です。必要なのは、他の回答で述べたようにテンプレートを使用するか、コンパイル時のブランチを用意することです。これは、IMOが(不要なテンプレートを導入するよりも)より適切なソリューションです。

struct is_64_bit :
    std::integral_constant<bool, sizeof(void*) == 8>
{};

namespace detail
{
    void my_memcpy(void* target, const void* source, std::size_t n, std::true_type)
    {
        std::cout << "64 bit memcpy" << std::endl;
    }


    void my_memcpy(void* target, const void* source, std::size_t n, std::false_type)
    {
        std::cout << "32 bit memcpy" << std::endl;
    }
}

void my_memcpy(void* target, const void* source, std::size_t n)
{
    my_memcpy(target, source, n, is_64_bit());
}
于 2012-09-27T16:26:41.847 に答える