2016-02-18 3 views
6

Рассмотрим следующий набор примеров.Можно ли записать переменное количество параметров в лямбда?

  1. Функция takeOnlyVoidFunction выполняет функцию с нулевыми аргументами и просто выполняет ее.
  2. Функция takeVariableArguments принимает переменное количество аргументов и выполняет функцию с использованием аргументов.
  3. Функция captureVariableArgs пытается преобразовать вторую функцию в форму лямбда, которая приемлема первой функцией, но не компилируется.

Как я могу сделать функцию captureVariableArgs компиляции и демонстрирует правильное поведение преобразования функции с переменным числом аргументов в замыкание без аргументов?

#include <stdio.h> 
#include <functional> 

void takeOnlyVoidFunction(std::function<void()> task) { 
    task(); 
} 

template<typename _Callable, typename... _Args> 
    void takeVariableArguments(_Callable&& __f, _Args&&... __args) { 
    __f(__args...); 
} 

// How can I make this function compile? 
template<typename _Callable, typename... _Args> 
    void captureVariableArgs(_Callable&& __f, _Args&&... __args) { 
    takeOnlyVoidFunction([=]() { __f(__args...);}); 
} 

void normalFunction(int a, int b) { 
    printf("I am a normal function which takes params (%d,%d)\n", a, b); 
} 

int main() { 
    int a = 7; 
    int b = 8; 
    takeVariableArguments(normalFunction, a, b); 
    takeOnlyVoidFunction([=](){ normalFunction(a,b);}); 
    captureVariableArgs(normalFunction, a, b); 
} 

Я бегу gcc 4.9.2. Вот ошибка компилятора, которую я вижу.

g++ -std=c++11 Test.cc -o Test 
Test.cc: In instantiation of ‘captureVariableArgs(_Callable&&, _Args&& ...)::<lambda()> [with _Callable = void (&)(int, int); _Args = {int&, int&}]’: 
Test.cc:16:38: required from ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’ 
Test.cc:16:50: required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’ 
Test.cc:28:45: required from here 
Test.cc:16:34: error: variable ‘__f’ has function type 
    takeOnlyVoidFunction([=]() { __f(__args...);}); 
           ^
Test.cc:16:34: error: variable ‘__f’ has function type 
Test.cc: In instantiation of ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’: 
Test.cc:16:50: required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’ 
Test.cc:28:45: required from here 
Test.cc:16:34: error: field ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>::<__f capture>’ invalidly declared function type 
In file included from Test.cc:2:0: 
/usr/include/c++/4.9/functional:2418:7: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’, is used but never defined [-fpermissive] 
     function<_Res(_ArgTypes...)>:: 
    ^

Update: более минимальный пример, демонстрирующий эту проблему.

#include <stdio.h> 

// How can I make this function compile? 
template<typename _Callable> 
void captureVariableArgs(_Callable&& __f) { 
    takeOnlyVoidFunction([=]{ __f(); }); 
} 

void normalFunction() { 
    printf("I am a normal function\n"); 
} 

int main(){ 
    captureVariableArgs(normalFunction); 
} 
+1

Ваш код компилируется и работает, в чем проблема, которую вы видите? – ixSci

+0

Функция с комментарием к ней не компилируется для меня. Вы на clang или gcc? – merlin2011

+0

Будет обновляться с ошибками компиляции за пару минут. – merlin2011

ответ

2

В качестве другого потенциального обходного пути для GCC, вместо того, чтобы использовать лямбда, вы могли бы использовать std::bind:

template <typename F, typename... Args> 
auto captureVariable(F&& f, Args&&... args) 
{ 
    return std::bind(std::forward<F>(f), std::forward<Args>(args)...); 
} 

Это работает для меня под GCC 4.9.3.

+0

На самом деле, я тоже это рассмотрел, и он скомпилировал его, но я не был уверен, эквивалентен ли он. Это семантически эквивалентно? – merlin2011

+0

Это не совсем семантически эквивалентно вашему примеру, поскольку вы копируете в аргументах, когда используете '[=]'; это должно фактически направить их. – Yuushi

+0

@Yuushi, я считаю, что 'bind' скопирует все свои аргументы. Он должен хранить их, чтобы они не могли просто переслать их. – ixSci

2

код в посте компилируется нормально с последними лязг & компиляторов MSVC, но все ГЦС отказываются скомпилировать его. Так что кажется ошибкой в ​​gcc. Тем не менее, я нашел способ сделать Gcc счастливым: просто не использовать «универсальную ссылку» на отзывной аргумент, как это:

template<typename _Callable, typename... _Args> 
int captureVariableArgs(_Callable _f, _Args&&... _args) { 
    return takeOnlyVoidFunction([=]() { _f(_args...);}); 
} 

Я не могу объяснить, почему НКУ не принимает вашу версию, хоть. Я не знаком с сообщением об ошибках в стиле gcc и не могу извлечь истинную причину из сообщения об ошибке. Но я думаю, что обходной путь оправдан, так как я не вижу никакой ценности в «универсальной ссылке» в этом случае. На самом деле, я не понимаю, почему вы используете его в args.

+0

Я использую универсальную ссылку, потому что я реализую библиотеку потоков и реализацию 'std :: thread' в gcc использует универсальную ссылку для аргументов, и я предполагаю, что она делает это для производительности и гибкости. – merlin2011

+0

Ну, это все равно копируется лямбдой с помощью '[=]'. Возможно, вы можете каким-то образом переместить его в список захвата, тем самым сохраняя только одну копию с подвижными типами. – PeterT

+0

@ merlin2011, в этой функции это бессмысленно - вы просто скопируете его с помощью списка захвата – ixSci

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