2015-02-01 3 views
3

Я очень рад, что нашел for_each_arg(...), что значительно облегчает работу с пакетами аргументов.Правильное использование `for_each_arg` - слишком большая пересылка?

template<class F, class...Ts> 
F for_each_arg(F f, Ts&&...a) { 
return (void)initializer_list<int>{(ref(f)((Ts&&)a),0)...}, f; 
} 

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

Чтение кода становится более трудным с чрезмерной интенсивностью.

struct UselessContainer 
{ 
    // Expects a perfectly-forwarded item to emplace 
    template<typename T> void add(T&&) { } 
}; 

// Creates an `UselessContainer` already filled with `mArgs...` 
auto makeUselessContainer(TArgs&&... mArgs) 
{ 
    using namespace std; 
    UselessContainer result; 

    for_each_arg 
    (
     [&result, &mArgs...] // Am I capturing the `mArgs...` pack correctly here? 
     (auto&& mX) // Am I passing the arguments to the lambda correctly here? 
     { 
      // Is this `forward` necessary? 
      result.add(forward<decltype(mX)>(mX)); 

      // Could it be replaced with 
      // `result.add(forward(mX));` 
      // ?    
     }, 
     forward<TArgs>(mArgs)... // I assume this `forward` is necessary. 
    ); 

    return result; 
} 

Все мои вопросы/сомнения выражены в комментариях в приведенном выше примере кода.

+0

Я не понимаю, почему вам нужно захватить пакет. Остальное выглядит нормально. Вы пересылаете аргументы 'for_each_arg', который пересылает их в лямбду, которая должна переслать их в' add() '. –

ответ

3

Каждый forward в вашем коде действительно необходим, чтобы идеально форматировать все аргументы до конца. Имена ссылок rvalue являются lvalues, поэтому, если вы не пересылаете каждый раз, когда вы передаете аргументы, информация о категории значений теряется.
Также невозможно вызвать forward без явного списка аргументов шаблона, поскольку параметр шаблона используется только в одном, не выводимом контексте. Фактически, шаблон функции, называемый без явного списка аргументов, не может выполнять задание.

Вы можете попробовать макрос несколько сократить код:

#define FORWARD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__) 

тогда становится

for_each_arg 
(
    // Removed superfluous capture 
    [&result] (auto&& mX) { 
     result.add(FORWARD(mX));  
    }, 
    FORWARD(mArgs)... 
); 

Также можно использовать макрос вместо for_each_arg в первую очередь:

#define FOR_EACH_ARG(...) (void)std::initializer_list<int>{((__VA_ARGS__),0)...} 

FOR_EACH_ARG(result.add(forward<TArgs>(mArgs))); 
+1

Слава богу в C++ 17, нам нужно только сделать '((void) result.add (forward (mArgs)), ...);' –

+0

@ T.C .: Является ли эта часть предложения сгибами? Как я могу узнать больше об этом? –

+1

@Deduplicator Не требуется, выражение fold также работает с пустыми пакетами (для определенных операторов, включая ','). –

1
for_each_arg (
    [&](auto&& mX){ 
    result.add(std::forward<decltype(mX)>(mX)); 
    }, 
    std::forward<TArgs>(mArgs)... 
); 

Просто захватите & при создании этого вида лямбда. Если вы должны перечислить, необходимо снять только &result.

forward<?> всегда используется с параметром типа.

Замечание Eric's for_each_arg несовершенно, и в основном это делается в 140 символов или меньше. ;) Его несовершенства мягкие и безвредные здесь.

Вот альтернатива:

Во-первых, это написать:

template<class...Fs> 
void do_in_order(Fs&&...fs){ 
    int _[]={0, 
    (((void)(std::forward<Fs>(fs)())),0)... 
    }; 
    (void)_; // kills warnings 
} 

он принимает ноль ARG лямбды, и запускает их слева направо.

Затем заменить вызов for_each_arg с:

do_in_order(
    [&]{ 
    result.add(std::forward<TArgs>(mArgs)); 
    }... 
); 

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

Порядок выражений в do_in_order гарантируется разделами [dcl.init] и [dcl.init.list] в n4296 8.5.4/4 8.5.4/1 8.5/15 8.5/1. Инициализация - это инициализация списка копий (8.5/15 и 8.5.4/1), представляет собой список инициализаторов скопированный начальный список "(8.5/1) и как таковой секвенирован слева направо (8.5.4/4).

+0

Не знал о возможных недостатках в 'for_each_arg'. Я действительно не забочусь о длительности его реализации, поэтому, если можно исправить эти недостатки, мне бы очень хотелось знать, как это сделать! 'for_each_arg' стал одной из моих любимых функций :) –

+0

@vitt должен взять' F' на 'F &&', чтобы покончить с возможной бесполезной копией. Возвращение 'F' является сомнительным, и если это произойдет, следует перенаправить (хорошо перемещенный в существующем коде). 'std :: ref (f)' бессмысленно - просто 'f'. В целом это часто требует дополнительной пересылки (сравните два выше) и шаблона. – Yakk

+1

Ну, 'std :: ref' не совсем бессмысленно, только в основном. Что делать, если вы хотите вызвать один и тот же элемент-указатель на весь список объектов? Помните, что 'ref (f) (...)' имеет семантику 'INVOKE (f, ...)'. – Deduplicator

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

  • Нет связанных вопросов^_^