5

問題: C++11 でプログラムを開発しています。右辺値参照と左辺値参照の両方を受け入れる関数を書きたいです。(つまり、ユニバーサル リファレンス)。

次の関数は、ユニバーサル参照パラメーターを受け入れます。

template<class T> void function(T&& t){/*SNIP*/}

ただし、すべてのタイプのパラメーターを受け入れます。関数の型安全性が損なわれます。特定のタイプのパラメーターを受け入れたい場合はどうすればよいですか?

これが私が考えることができる解決策です:

void function(Class& t){/*SNIP*/}
void function(Class&& t){ function(t); }

しかし、それは醜いです。受け入れるパラメーターを変更したり、関数名を変更したりする場合は、関数の両方のバージョンを更新する必要があります。これよりも優れた同等のものはありますか?

編集:問題は解決しました。お二人ともよく答えてくれました。感謝の気持ちを表すために、両方の回答に +1 を投票しました。この質問は数日間放置します。投票数が最も多い回答が採用されます。

EDIT2:私は次のコードで終わります:

template < class T,
class=typename std::enable_if<std::is_same<Class, typename std::decay<T>::type>::value>::type //Dummy template parameter
>
void function(T&&){}

EDIT3:この目的のためにマクロ定義を書きました:

#define uRefType(T, typeLimit) class T, class=typename std::enable_if<std::is_same<typename std::decay<T>::type, typeLimit>::value>::type

使用例:

template< uRefType(T, Class) > void function(T&&){}
4

2 に答える 2

4

これを行う 1 つの方法は、 を使用することstd::enable_ifです。type_traitsこれは、ヘッダーによって提供される構造体です。これは、ブール条件がコンパイル時に true と評価される場合のenable_if<A,B>::type型になるように定義されています。それ以外の場合は空です。BA

したがって、関数テンプレートがある場合

template <typename T>
void fun(T &&)
{ /*...*/ }

Tが特定の型の場合にのみ定義されるようにしたい場合はenable_if<...>::type、戻り値の型の代わりに構造を使用できます (ここではvoid)。ブール条件Aは次のように定義されます: Tisint、型Bは関数の元の戻り値の型として定義されます (ここではvoid)。

したがって、 is のfun場合にのみ定義したい場合Tint、次のようになります。

#include <type_traits>

template <typename T>
typename std::enable_if<std::is_same<int,typename std::decay<T>::type>::value,void>::type
fun(T &&)
{ }

int main()
{
  int    lvali = 3;
  double lvald = 3.3;

  fun(3);
  fun(lvali);

  // fun(3.3);      // this won't be accepted (not an int)
  // fun(lvald)     // this won't be accepted (not an int)

  return 0;
}

ブール条件が次のように定義されていることに注意してください (std::読みやすくするために を省略しています)。

is_same<int,typename decay<T>::type>::value

decayステートメントは、これが or (およびさらにいくつかの特殊なケース) に関係なく機能することを保証するために使用さTintますint &


その他の注意事項: この種のトリックは、問題の関数の定義が右辺値と左辺値の両方で同じである場合にのみ、本当に役立ちます。多くの場合、そうではありません (右辺値の場合は移動を実装しますが、左辺値の場合は実装しないか、または同様のものであるため)。

両方の定義が実際に同じである典型的な状況は、関数本体が非常に短く、引数をさらに別の (オーバーロードされている可能性がある) 関数呼び出しに転送する以外に何もしない場合です。

template <typename T>
void fun(T &&obj)
{ other_fun(std::forward<T>(obj)); }

その場合、 の宣言により、最終的に特定の型のみが受け入れられることが保証されるため、おそらく何らかのenable_ifトリックを使用しなくても問題ありません。other_fun

于 2013-02-17T07:56:56.400 に答える
1

std::enable_ifで使用std::is_same:

template<class T, 
         class = typename std::enable_if<std::is_same<float, T>::value>::type>
void function(T&& t){/*SNIP*/}

そして(私が言及するのを忘れたjogojapanの答えで最初に述べたように)std::decayonTを使用します。 asは aとT&同じ型ではなく、 T` と同じではありません。Tconst T

template<
  class T, 
  class = typename std::enable_if<
                       std::is_same<float,
                                    typename std::decay<T>::type>::value>::type>
void function(T&& t){/*SNIP*/}

http://ideone.com/ztkHsfでデモを参照してください。

于 2013-02-17T07:45:02.620 に答える