【问题标题】:Workaround for variadic lambda capture可变参数 lambda 捕获的解决方法
【发布时间】:2013-11-29 04:05:35
【问题描述】:

我在这里看到了一些与在 lambda 中捕获可变参数的 gcc 错误有关的问题。例如:Does lambda capture support variadic template argumentsCompiler bug, or non standard code? - Variadic template capture in lambda。我有以下人为的例子来说明我正在尝试做的事情

#include <iostream>
#include <functional>

class TestVariadicLambda {
public:

    template<typename... Args>
    std::function<void()> getFunc(Args... args) {
        return [=]{ printArgs(args...); };
    }

    template<typename T, typename... Args>
    void printArgs(T value, Args... args) {
        std::cout << value << ", ";
        printArgs(args...);
    }

    void printArgs() {std::cout << "\n";}
};

在 gcc 4.8.2 中出现以下错误:

../src/TestVariadicLambda.h: In lambda function:
../src/TestVariadicLambda.h:9:25: error: parameter packs not expanded with ‘...’:
   return [=]{ printArgs(args...); };
                         ^
../src/TestVariadicLambda.h:9:25: note:         ‘args’
../src/TestVariadicLambda.h:9:29: error: expansion pattern ‘args’ contains no argument packs
   return [=]{ printArgs(args...); };
                         ^

我的问题是我该如何解决这个问题,因为它在 gcc4.8 中不起作用

【问题讨论】:

  • 它对你帮助不大,但代码用 clang 3.4 (trunk) 编译。
  • 我刚刚用 g++4.9 快照对其进行了测试,它也可以在那里工作。
  • 这是个好消息,但不幸的是,如果您需要使用 gcc 4.8,它对您没有帮助
  • 看看我的愚蠢工作我猜..
  • 我添加了使用 GCC 4.7(已测试)编译的答案,对 GCC 4.8(已测试)可能有点晚了,也许它仍然对您有用。您应该注意,使用这种代码 clang 会产生程序集膨胀,这是 GCC 性能更好的少数情况之一。

标签: c++ c++11 lambda variadic-templates


【解决方案1】:

以下将起作用。

#include <iostream>
#include <functional>
#include <vector>

std::string to_string(const char* s)
{
    return s;
}

class Test
{
    private:
        void print() {}

    public:
        template<typename T, typename... Args>
        void print(T value, Args... args)
        {
            std::cout << value << "\n";
            print(args...);
        }

        template<typename... Args>
        std::function<void()> getFunc(Args... args)
        {
            using namespace std;
            std::vector<std::string> ArgumentList;
            std::initializer_list<int> {(ArgumentList.push_back(to_string(args)), 0)...};
            return [=] {for (const auto &t : ArgumentList){print(t);}};
        }
};

int main()
{
    Test().getFunc("Hey", 1, 2, 3.0f)();
}

【讨论】:

  • 没有静态解决方法? (即没有 std::vector,而是常规数组?)
【解决方案2】:

这是一个至少在 GCC 4.8.0 上静态工作的解决方法(也应该在 VisualStudio 和 clang 上工作)

(代码主要来自this answer,我所做的是将参数包装在一个元组中,然后将元组用作旁路)

#include <iostream>
#include <functional>
#include <tuple>
using namespace std;

// ------------- UTILITY---------------
template<int...> struct index_tuple{}; 

template<int I, typename IndexTuple, typename... Types> 
struct make_indexes_impl; 

template<int I, int... Indexes, typename T, typename ... Types> 
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> 
{ 
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; 
}; 

template<int I, int... Indexes> 
struct make_indexes_impl<I, index_tuple<Indexes...> > 
{ 
    typedef index_tuple<Indexes...> type; 
}; 

template<typename ... Types> 
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> 
{};


template<class Ret, class... Args, int... Indexes > 
Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup) 
{ 
    return pf( forward<Args>( get<Indexes>(tup))... ); 
} 

template<class Ret, class ... Args> 
Ret apply(Ret (*pf)(Args...), const tuple<Args...>&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup));
}

template<class Ret, class ... Args> 
Ret apply(Ret (*pf)(Args...), tuple<Args...>&&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}



/// ------------------- REAL CODE --------

void printArgs() {std::cout << "\n";}

template<typename T, typename... Args>
void printArgs(T value, Args... args) {
    std::cout << value << ", ";
    printArgs(args...);
}

template<typename... Args>
std::function<void()> getFunc(Args... args) {
    std::tuple<Args...> tup(args...);
    return [=]{ apply<void, Args...>(printArgs, tup); };
}


int main(){
    auto f = getFunc<int,float,const char*>(4,5.4f,"hello");
    f();
    return 0;
}

你为什么不使用std::bind?基本上 std::bind 可以为您创建一个带有您想要的参数的lambda(只是语法糖,为您选择最可行的方式:))

【讨论】:

  • 使用 GCC 4.7+ clang 3.3+ 和 Visual Studio 2013 update 3 编译(并运行)正常
猜你喜欢
  • 1970-01-01
  • 2020-03-21
  • 1970-01-01
  • 2013-03-08
  • 2012-12-31
  • 1970-01-01
  • 2017-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多