2017-02-10 4 views
1

скажем у меня есть функция Foo, которая принимает два не-примитивный объект различных типов, например:передать аргументы функции в Си ++ без определенного порядка

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

в предположении, что размещение этих аргументов не имеет никакого влияния на функция выводит тогда foo (h, d) = foo (d, h) теоретически. но C++ не позволяет этого, если я не перегружать эту функцию:

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

но эта перегрузка становится боль в приклад, если число аргументов увеличивается (например, 6 перегрузки для 3 аргументов и так далее).

Мой вопрос в том, есть ли какой-либо простой способ добиться согласования аргументов прохождения не в определенном порядке без повторной перегрузки?

+4

Просто из любопытства, почему кто-то хочет передать параметры функции в неправильном порядке? – DyZ

+0

Зачем вам нужна эта гибкость? Если вы его построите, вы можете передать их в определенном порядке. –

+0

, если вы знаете, что функция принимает 3 разных типа аргументов, было бы нецелесообразно передавать их в любом порядке, а не проверять функцию prototyoe? – Arash

ответ

0

As far as I can tell, как представляется, не быть способом тиражирования Python стиль именованных аргументов в C++ (помимо способов, перечисленных в предоставленном Software Engineering нити.

1

В C++ 17, мы имеем std::get<T>(std::tuple). This могут быть объединены с 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 Спасибо, исправлено. (Вместо повторного выполнения/shimming 'get', я переключился на pass-by-value.) – Potatoswatter

+0

Мне очень нравится ваш ответ. Единственная проблема, которую я вижу, это то, что это UB, если у вас есть тот же тип дважды из-за того, как работает 'std :: get'. Я ошибаюсь? Это может быть сильным ограничением. – skypjack

1

Вообще говоря, я t лучше уменьшить количество параметров, переданных функции. Есть два способа сделать то, что вы хотите.

  • Создайте класс, который содержит эти не примитивные данные в качестве переменных-члена. После того, как эти члены инициализированы, вы можете вызвать функцию-член, которая работает с данными этого члена.

  • Инкапсулируйте эти не примитивные данные в объект и передайте этот объект по ссылке на вашу функцию.

В любом случае вам не нужно беспокоиться о порядке аргументов, пока все аргументы инициализированы.

+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) 
{ 

} 

При необходимости можно переделать объект с помощью m_type в MYFUNCTION или реализации глобальной функции на родительский класс.

Другим способом может быть использование шаблонов.

+0

Это исключает любое понятие безопасности времени компиляции. Для извлечения типов вам понадобится dynamic_casts. –

1

Вполне возможно, изменить порядок п аргументов с O (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 

Do не толковать это как подтверждение этой техники. Скорее: даже если это возможно, не делайте этого.

0

Есть ли какой-либо простой способ получить кусочек передающих аргументов не в определенном порядке без повторной перегрузки?

Да, вы можете это сделать, но поскольку вы не сказали, каково ваше точное использование этой функции, я не могу дать вам общее решение. В любом случае, для всех людей, заявляющих в комментариях, что поведение, которое требуется автору вопроса, неверно, плохо, глупо и т. Д., Пожалуйста, рассмотрите следующий фрагмент кода, который работает, и я вижу потенциал в этом.

Предположим, что у нас есть класс, у которого есть поля, в которых хранятся некоторые значения, и мы хотим иметь сеттеры (и, вероятно, получатели) для доступа к этим значениям (для ясности я представил только минимальную версию).

Таким образом, этот класс будет:

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

Этот класс не имеет сеттеров или добытчик, потому что мы собираемся, чтобы обернуть его в другом классе, в Data классе.

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 

Как я уже сказал, я не знаю, соответствует ли это вашим потребностям, но это всего лишь пример, как это сделать, и это может быть полезно для вас.

Несмотря на то, что значения, заданные в заданном порядке (в сочетании с произвольным количеством значений), имеют преимущество, что вы можете установить только те значения, которые необходимы.

Смежные вопросы