31

クラステンプレートパラメータに基づいて、どのバージョンのメンバー関数が呼び出されるかを判断しようとしています。私はこれを試しました:

#include <iostream>
#include <type_traits>

template<typename T>
struct Point
{
  void MyFunction(typename std::enable_if<std::is_same<T, int>::value, T >::type* = 0)
  {
    std::cout << "T is int." << std::endl;
  }

  void MyFunction(typename std::enable_if<!std::is_same<T, int>::value, float >::type* = 0)
  {
    std::cout << "T is not int." << std::endl;
  }
};

int main()
{
  Point<int> intPoint;
  intPoint.MyFunction();

  Point<float> floatPoint;
  floatPoint.MyFunction();
}

これは、「Tがintの場合は最初のMyFunctionを使用し、Tがintでない場合は2番目のMyFunctionを使用する」と思っていましたが、「エラー:「structstd::enable_if」に「type」という名前の型がありません」というコンパイラエラーが発生します。 。ここで私が間違っていることを誰かが指摘できますか?

4

6 に答える 6

32

enable_ifテンプレート引数の置換によってエラーが発生したために機能します。そのため、置換はオーバーロード解決セットから削除され、他の実行可能なオーバーロードのみがコンパイラによって考慮されます。

Tあなたの例では、テンプレート引数はその時点ですでにわかっているため、メンバー関数をインスタンス化するときに置換は発生しません。試行していることを実現する最も簡単な方法は、デフォルトのダミーテンプレート引数を作成し、Tそれを使用してSFINAEを実行することです。

template<typename T>
struct Point
{
  template<typename U = T>
  typename std::enable_if<std::is_same<U, int>::value>::type
    MyFunction()
  {
    std::cout << "T is int." << std::endl;
  }

  template<typename U = T>
  typename std::enable_if<std::is_same<U, float>::value>::type
    MyFunction()
  {
    std::cout << "T is not int." << std::endl;
  }
};

編集:

HostileForkがコメントで言及しているように、元の例では、ユーザーがメンバー関数のテンプレート引数を明示的に指定して、誤った結果を取得する可能性があります。以下は、メンバー関数の明示的な特殊化がコンパイルされないようにする必要があります。

template<typename T>
struct Point
{
  template<typename... Dummy, typename U = T>
  typename std::enable_if<std::is_same<U, int>::value>::type
    MyFunction()
  {
    static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
    std::cout << "T is int." << std::endl;
  }

  template<typename... Dummy, typename U = T>
  typename std::enable_if<std::is_same<U, float>::value>::type
    MyFunction()
  {
    static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
    std::cout << "T is not int." << std::endl;
  }
};
于 2012-11-15T16:31:19.067 に答える
6

簡単な解決策は、委任を使用してプライベート関数を実行することです。

template<typename T>
struct Point
{

  void MyFunction()
  {
     worker(static_cast<T*>(nullptr)); //pass null argument of type T*
  }

private:

  void worker(int*)
  {
    std::cout << "T is int." << std::endl;
  }

  template<typename U>
  void worker(U*)
  {
    std::cout << "T is not int." << std::endl;
  }
};

の場合、タイプTがであることが判明したためint、最初のworker関数が呼び出されます。他のすべての場合、workerのテンプレートバージョンが呼び出されます。static_cast<T*>(0)int*

于 2012-11-15T16:39:27.463 に答える
1

enable_if推定された関数テンプレート引数または特殊なクラステンプレート引数に対してのみ機能します。T = int明らかに固定されているため、2番目の宣言は誤っているため、実行していることは機能しません。

これがその方法です。

template <typename T>
void MyFreeFunction(Point<T> const & p,
                    typename std::enable_if<std::is_same<T, int>::value>::type * = nullptr)
{
    std::cout << "T is int" << std::endl;
}

// etc.

int main()
{
    Point<int> ip;
    MyFreeFunction(ip);
}

Point別の方法は、さまざまなタイプに特化するTか、上記の無料の関数をネストされたメンバーテンプレートラッパーに配置することです(これはおそらくより「適切な」ソリューションです)。

于 2012-11-15T16:32:47.407 に答える
1

Praetorianの提案に基づくと(ただし、関数のreturnタイプを変更せずに)、これは機能するようです。

#include <iostream>
#include <type_traits>

template<typename T>
struct Point
{
  template<typename U = T>
  void MyFunction(typename std::enable_if<std::is_same<U, int>::value, U >::type* = 0)
  {
    std::cout << "T is int." << std::endl;
  }

  template<typename U = T>
  void MyFunction(typename std::enable_if<!std::is_same<U, int>::value, float >::type* = 0)
  {
    std::cout << "T is not int." << std::endl;
  }
};

int main()
{
  Point<int> intPoint;
  intPoint.MyFunction();

  Point<float> floatPoint;
  floatPoint.MyFunction();
}
于 2012-11-15T16:44:56.193 に答える
1

これは@Praetorianの解決策に従っていると思いますが、もっと簡単だと思います。

template<typename T>
struct Point
{
    template<typename U = T>
    std::enable_if_t<std::is_same<U, T>::value && std::is_same<T, int>::value>
    MyFunction()
    {
        std::cout << "T is int." << std::endl;
    }

    template<typename U = T>
    std::enable_if_t<std::is_same<U, T>::value && std::is_same<T, float>::value>
    MyFunction()
    {
        std::cout << "T is not int." << std::endl;
    }
};
于 2020-09-19T17:42:13.337 に答える
0

以下のポイントテンプレートは、テンプレート引数Tとしてintまたはfloatを使用してのみインスタンス化できます。

質問に答えるには:ここでworker()は、method()呼び出しのテンプレートパラメーターに正確に応じて呼び出されますが、それでも型を制御できます。

    template<typename T>
    struct Point
    {
        static_assert (
              std::is_same<T, int>()  ||
              std::is_same<T, float>()
            );

        template<typename U>
        void method(U x_, U y_)
        {
            if constexpr (std::is_same<T, U>()) {
                worker(x_, y_);
                return;
            }
            // else 
            worker(
                static_cast<T>(x_),
                static_cast<T>(y_)
            );
            return ;
        }


    private:

        mutable T x{}, y{};

        void worker(T x_, T y_)
        {
            // nothing but T x, T y
        }

    };

上記のworker()は、静的として宣言されている場合でももちろん機能します。いくつかの正当な理由があります。上記の他のいくつかの拡張は可能です(そして単純です)が、答えに固執しましょう。

于 2018-05-18T13:10:59.650 に答える