web-dev-qa-db-ja.com

std :: enable_if:パラメーターとテンプレートパラメーター

整数または倍精度、あるいはその両方に固有の関数を必要とする入力チェッカーを構築しています(たとえば、「isPrime」は整数でのみ使用可能である必要があります)。

パラメータとしてenable_ifを使用している場合、完全に機能しています。

template <class T>
class check
{
public:
   template< class U = T>
   inline static U readVal(typename std::enable_if<std::is_same<U, int>::value >::type* = 0)
   {
      return BuffCheck.getInt();
   }

   template< class U = T>
   inline static U readVal(typename std::enable_if<std::is_same<U, double>::value >::type* = 0)
   {
      return BuffCheck.getDouble();
   }   
};

しかし、私がそれをテンプレートパラメータとして使用している場合( http://en.cppreference.com/w/cpp/types/enable_if で示されているように)

template <class T>
class check
{
public:
   template< class U = T, class = typename std::enable_if<std::is_same<U, int>::value>::type >
   inline static U readVal()
   {
      return BuffCheck.getInt();
   }

   template< class U = T, class = typename std::enable_if<std::is_same<U, double>::value>::type >
   inline static U readVal()
   {
      return BuffCheck.getDouble();
   }
};

次に、次のエラーが発生します。

error: ‘template<class T> template<class U, class> static U check::readVal()’ cannot be overloaded
error: with ‘template<class T> template<class U, class> static U check::readVal()’

2番目のバージョンの何が問題なのか理解できません。

32
Loïc Février

デフォルトのテンプレート引数はテンプレートの署名の一部ではありません(したがって、両方の定義が同じテンプレートを2回定義しようとします)。ただし、それらのパラメータタイプはシグニチャの一部です。だからあなたはすることができます

template <class T>
class check
{
public:
   template< class U = T, 
             typename std::enable_if<std::is_same<U, int>::value, int>::type = 0>
   inline static U readVal()
   {
      return BuffCheck.getInt();
   }

   template< class U = T, 
             typename std::enable_if<std::is_same<U, double>::value, int>::type = 0>
   inline static U readVal()
   {
      return BuffCheck.getDouble();
   }
};

問題は、コンパイラが同じメソッドの2つのオーバーロードを認識し、両方に同じ引数(この場合はnone)と同じ戻り値が含まれていることです。そのような定義を提供することはできません。これを行う最もクリーンな方法は、関数の戻り値にSFINAEを使用することです。

template <class T>
class check
{
public:
   template< class U = T>
   static typename std::enable_if<std::is_same<U, int>::value, U>::type readVal()
   {
      return BuffCheck.getInt();
   }

   template< class U = T>
   static typename std::enable_if<std::is_same<U, double>::value, U>::type readVal()
   {
      return BuffCheck.getDouble();
   }
};

このようにして、2つの異なるオーバーロードを提供します。 1つはintを返し、もう1つはdoubleを返し、特定のTを使用してインスタンス化できるのは1つだけです。

9
mfontanini

この質問はstd::enable_ifに関するものですが、enable_ifを使用せずに同じ問題を解決するための代替ソリューションを提供したいと思います。それはC++ 17を必要とします

template <class T>
class check
{
public:
   inline static T readVal()
   {
        if constexpr (std::is_same_v<T, int>)
             return BuffCheck.getInt();
        else if constexpr (std::is_same_v<T, double>)
             return BuffCheck.getDouble();
   }   
};

このコードは、実行時に記述するかのように見えます。すべてのブランチは構文的に正しい必要がありますが、セマンティクスは正しい必要はありません。この場合、Tがintの場合、getDoubleはコンパイラによってチェック/使用されないため、コンパイルエラー(または警告)を引き起こしません。

関数の戻り値の型が複雑である場合は、いつでもautoを戻り値の型として使用できます。

2
JVApen