6

1 つのコードは 1,000 語に値します。

int main()
{
    // All of the following calls return true:
    AreEqual(1, 1);
    AreEqual(1, 1, 1);
    AreEqual(1, 1, 1, 1);
    AreEqual(1, 1, 1, 1, 1);

    // All of the following calls return false:
    AreEqual(1, 2);
    AreEqual(1, 2, 1);
    AreEqual(1, 7, 3, 1);
    AreEqual(1, 4, 1, 1, 1);    
}

任意の数の引数を受け入れる関数 AreEqual() を実装する方法は?

些細だが退屈な解決策は、オーバーロードによるものです。

bool AreEqual(int v1, int v2);
bool AreEqual(int v1, int v2, int v3);
bool AreEqual(int v1, int v2, int v3, int v4);
......

別の簡単ですが実行できない解決策は次のとおりです。

bool AreEqual(...);

呼び出し元が別の引数 (引数カウントまたは終了マーカー) を追加して引数の数を指定する必要があるため、この解決策は実行できません。

さらに別の方法は、可変個引数のテンプレート引数を使用することです

template<class... Args>
bool AreEqual(Args... args)
{
    // What should be placed here ???
}
4

5 に答える 5

11

テンプレートを使用して実装する方法は次のとおりです。

#include <iostream>
#include <iomanip>

template<class T0>
bool AreEqual(T0 t0) { return true; }

template<class T0, class T1, class... Args>
bool AreEqual(T0 t0, T1 t1, Args ... args) {
  return t0 == t1 && AreEqual(t1, args...);
}


int main () {
  std::cout << std::boolalpha;

    // All of the following calls return true:
  std::cout<< AreEqual(1, 1) << "\n";
  std::cout<< AreEqual(1, 1, 1) << "\n";
  std::cout<< AreEqual(1, 1, 1, 1) << "\n";
  std::cout<< AreEqual(1, 1, 1, 1, 1) << "\n\n";

    // All of the following calls return false:
  std::cout<< AreEqual(1, 2) << "\n";
  std::cout<< AreEqual(1, 2, 1) << "\n";
  std::cout<< AreEqual(1, 7, 3, 1) << "\n";
  std::cout<< AreEqual(1, 4, 1, 1, 1)  << "\n";
}

これらのバリエーションが使用に適しているかどうかを検討する必要があります。

  • 値渡しの代わりに参照を使用する
  • 非可変個引数テンプレートが 1 つではなく 2 つのパラメーターを取るようにします。


別の方法として、再帰的でないバージョンを次に示します。悲しいことに、それは短絡的ではありません。非再帰的な短絡バージョンを見るには、他の回答を参照してください。

template<typename T, typename... Args>
bool AreEqual(T first, Args... args)
{
  return std::min({first==args...});
}


最後に、このバージョンは繊細さが欠けている点で魅力的です。

template<typename T, typename... Args>
bool AreEqual(T first, Args... args)
{
  for(auto i : {args...})
    if(first != i)
      return false;
  return true;
}
于 2013-01-29T20:55:28.297 に答える
4

あなたは何らかの理由でそれを行うための賢明な方法を除外しているように見えるので、あなたはまた使用することを試みることができますstd::initializer_list

template<typename T>
bool AreEqual(std::initializer_list<T> list) {
    ...
}

次に、次のように呼びます。

AreEqual({1,1,1,1,1});
于 2013-01-29T21:00:08.253 に答える
3

非再帰バージョンは次のとおりです。

template <typename T> using identity = T;

template<typename T, typename... Args>
bool AreEqual(T first, Args... args)
{
  bool tmp = true;
  identity<bool[]>{tmp?tmp=first==args:true ...};
  return tmp;
}

最適化をオンにすると、すべての引数が最初の引数と比較されるため、動作が異なる可能性があることを除いて、再帰関数と比較してオーバーヘッドはありません。

于 2013-01-29T21:04:58.160 に答える
1

varadicテンプレートには、特殊化に関する再帰が必要です。

template<class A> //just two: this is trivial
bool are_equal(const A& a, const A& b)
{ return a==b; } 

template<class A, class... Others>
bool are_equal(const A& a, const A& b, const Others&... others)
{ return are_equal(a,b) && are_equal(b,others...); }

本質的に、are_equalがネストされるたびに、他の人は... estinguishまで1つずつ短くなり、関数は2つの引数をバインドします。

:とAの両方の型として使用することにより、また、の最初の引数は常にAと一致する必要があるため、実際には、同じ型のすべての引数のみを受け入れるようになります(少なくとも、最初の型に変換可能)口論)。abOthersare_equal(...)

この制約は一般的に便利ですが、AとをBタイプとして使用することで制限を緩和できます 。これにより、関数は、ペアごとに存在するタイプのすべてのセットで機能します。aboperator==

template<class A, class B> //just two: this is trivial
bool are_equal(const A& a, const B& b)
{ return a==b; } 

template<class A, class B, class... Others>
bool are_equal(const A& a, const B& b, const Others&... others)
{ return are_equal(a,b) && are_equal(b,others...); }
于 2013-01-29T20:58:43.753 に答える