2017-02-10 7 views
1

はのiは、異なるタイプの二つの非プリミティブ・オブジェクトを取る関数foo、例えばを持っているとしましょう:これらの引数の配置を想定し特定の順序なしでC++の関数に引数を渡す

void foo(House h, Dog d) {// implementation } 

はには影響しません関数出力は理論的にfoo(h、d)= foo(d、h)となる。

void foo (Dog d, House h) {// implementation } 

が、引数の数が増加する(例えば6 3つの引数のオーバーロードなど)場合は、この過負荷は、尻の痛みになる:Iにより、この機能をオーバーロードしない限りが、C++はこれを許可しません。

私の質問は、反復的なオーバーロードなしに、特定の順序ではなく引数を渡すという手段を習得する簡単な方法はありますか?

+4

ちょうど好奇心から、なぜ誰かが間違った順序で関数にパラメータを渡したいのですか? – DyZ

+0

なぜこの柔軟性が必要ですか?それを構築する場合、定義された順序でそれらを渡すことができます。 –

+0

関数が3つの異なる型の引数を取ることを知っているなら、関数prototyoeを調べるのではなく、順番どおりにそれらを渡すのが便利ではないでしょうか? – Arash

答えて

0

As far as I can tell、提供ソフトウェアエンジニアリングスレッドに記載されている方法を超えて(C++でPythonのスタイルキーワード引数を複製する方法があるように思えません。C++ 17では

1

、我々はstd::get<T>(std::tuple)を持っている。このstd::forward_as_tupleと組み合わせることができます。

template< typename ... unordered_arg > 
void foo(std::tuple< unordered_arg ... > arg_set) { 
    House h = std::get< House && >(std::move(arg_set)); 
    Dog d = std::get< Dog && >(std::move(arg_set)); 

    // implementation 
} 

template< typename ... arg > 
void foo(arg ... a) 
    { foo_impl(std::forward_as_tuple(std::move(a) ...)); } 

(。それは限り、それの相互排他的な部分は毎回アクセスされるmove(arg_set)を行う乗算する大丈夫だ)

+0

タプルには参照が含まれており、ベアタイプを '取得 'しようとします。あなたは、このアプローチを動作させるためにそれらのパラメータにマッチさせる前に、型を崩壊させる独自の 'get'型をタプルに書く必要があります。 –

+0

@yurikilochekありがとうございました。 ( 'get'を再実装するのではなく、値渡しに切り替えました。) – Potatoswatter

+0

私は本当にあなたの答えが好きです。私が見ることができる唯一の問題は、 'std :: get'の仕組みのために同じ型を2回持っているとUBだということです。私が間違っている?これは強力な制約となる可能性があります。 – skypjack

1

は一般的に言って、私は関数に渡されるパラメータの数を減らす方が良いです。あなたが望むことをするには2つの方法があります。

  • これらの非プリミティブデータをメンバー変数として持つクラスを作成します。これらのメンバが初期化されると、これらのメンバデータで動作するメンバ関数を呼び出すことができます。

  • これらの非プリミティブデータをオブジェクトにカプセル化し、このオブジェクトを関数に渡して渡します。

あなたは、引数の順序を心配する限り、すべての引数が初期化される必要はありませんいずれかの方法。

+0

私はカプセル化が解決策であるかもしれないことに同意しますが、スタンドアロン機能ごとにクラスや構造を作ることは実用的ではありません。それは...ですか?多分私は間違っているかもしれないし、多分私は悪い質問をしました。 – Arash

+0

いいえ、私はスタンドアロン機能ごとにクラスまたは構造を作成することを主張していません。基本的には、どのデータがどのデータで動作しているかを特定する必要があり、これらのデータをカプセル化された形式でまとめることができるので、関心のあるデータで動作する関連メンバ関数を持つことができます。クラスまたは関数は単一の責任を持つ必要があります。 – Rishi

+0

あなたは、このクラスのコンストラクタに引数をどの順序で渡しますか? – user12341234

0

あなたは、この例のように

class Object 
{ 
public: 
    enum Type { Dog, House}; 
    Type m_type; 
}; 

class Dog : public Object 
{ 

}; 

class House : public Object 
{ 

}; 

void myFunction(const Object& a, const Object& b) 
{ 

} 

ことを行うために階層を使用すると、あなたはMyFunctionの内m_typeを使用してオブジェクトを書き直すか、親クラスでグローバル関数を実装することができ、必要に応じてそれを行うことができます。

テンプレートを使用する別の方法があります。

+0

これは、コンパイル時の安全性の概念を捨て去ります。タイプを取得するにはdynamic_castsが必要です。 –

1

それはO(n)のラッパーで再n次の引数にはかなり可能です:

#include <iostream> 
using namespace std; 
struct A { int a; A(int a) : a(a) {} }; 
struct B { int b; B(int b) : b(b) {} }; 
struct C { int c; C(int c) : c(c) {} }; 
struct D { int d; D(int d) : d(d) {} }; 
static void foo(A a, B b, C c, D d) { cout << a.a << " " << b.b << " " << c.c << " " << d.d << endl; } 
template<class ...Args> struct Foo { void operator()(Args...); }; 
template<class ...Args> static void foo(Args ...args) { Foo<Args...>()(args...); } 
template<class T, class U> struct Foo<T, U, C, D> { void operator()(T t, U u, C c, D d) { foo(u, t, c, d); } }; 
template<class T, class U, class V> struct Foo<T, U, V, D> { void operator()(T t, U u, V v, D d) { foo(v, t, u, d); } }; 
template<class T, class U, class V, class W> struct Foo<T, U, V, W> { void operator()(T t, U u, V v, W w) { foo(w, t, u, v); } }; 
int main() { 
    foo(A(1), B(2), C(3), D(4)); 
    foo(D(5), C(6), B(7), A(8)); 
    return 0; 
} 

(。の機能が部分的に専門的なことができないので、ラッパークラスFooが必要です)

$ c++ -std=c++11 a.cc 
$ ./a.out 
1 2 3 4 
8 7 6 5 

ドゥこれをこの手法の裏付けと解釈してはいけません。むしろ、可能であれば、これをしないでください。

0

反復的なオーバーロードなしに特定の順序ではなく引数を渡すという簡単な方法はありますか?

はい、これを行うことはできますが、この機能の正確な使い方がわからないため、私はこれに汎用的な解決方法を教えていただけません。とにかく、質問の著者が必要とする行動が間違っている、悪い、愚かなものであるとコメントで主張するすべての人々のために、作品の次のコードを検討してください。

いくつかの値を格納するフィールドを持つクラスがあり、私たちはこれらの値にアクセスするためのセッター(おそらくゲッターも)を持たせたいとしましょう。

ので、このクラスは次のようになります。私たちは、別のクラスにDataクラスを、それをラップしようとしているので、このクラスは、セッターやゲッターを持っていない

struct A { 
    int g1 {}; 
    int g2 {}; 
    int g3 {}; 
}; 

struct Data { 
    template <typename... Ts> 
    void set (Ts&&... ts) { 
     set_impl (std::forward<Ts> (ts)...); 
    } 

    A a; 
private: 
    template <typename T> 
    void set_impl (T&& t) { 
     a.*T::mem = t.v; // (X) 
    } 

    template <typename T, typename K, typename... Ts> 
    void set_impl (T&& t, K&& k, Ts&&... ts) { 
     set_impl (std::forward<T> (t)); 
     set_impl (std::forward<K> (k), std::forward<Ts> (ts)...); 
    } 
}; 

は、そこでここでは、異なるタイプの任意の数または引数を取り、それらのそれぞれがすることができます(この例ではなければなりません)setメンバ関数を持つクラスを持っています。 Aフィールドを(X)のように設定するには、Aメンバーへのポインタを持つタイプのオブジェクトを渡す必要があります。だから、以下のクラスを見てください。

struct G { 
    G (int c = {}) : v {c} {} 

    int v; 
}; 

struct G1 : G { using G::G; static constexpr int A::* mem = &A::g1; }; 
struct G2 : G { using G::G; static constexpr int A::* mem = &A::g2; }; 
struct G3 : G { using G::G; static constexpr int A::* mem = &A::g3; }; 

すべてGx機能はAフィールドの値を設定するためにここにいると、それらのそれぞれは、設定しようとしているフィールドを知っています。結果を示すための

ヘルパー関数は次のとおりです。

void show (const A& v) { 
    std::cout << "g1 = " << v.g1 << std::endl; 
    std::cout << "g2 = " << v.g2 << std::endl; 
    std::cout << "g3 = " << v.g3 << std::endl; 
} 

そして最後に、用法:私たちに出力できます

Data d; 
d.set (G1 {10}, G2 {20}, G3 {30}); 
show (d.a); 

Data p; 
p.set (G3 {40}, G1 {-30}, G2 {120}); 
show (p.a); 

g1 = 10 
g2 = 20 
g3 = 30 
g1 = -30 
g2 = 120 
g3 = 40 

私が言ったように、私はこれがあなたのニーズに合っているかどうかはわかりませんが、これはどのようにこれを行うかの例に過ぎず、おそらくあなたの役に立つかもしれません。

ただし、値を指定しない順序(値の任意の数と組み合わせて)に入れると、必要な値だけを設定できるという利点があります。

関連する問題