c++ - 将 lambda 转换为 std::tr1::function

标签 c++ functional-programming lambda c++11 tr1

使用带有 tr1 服务包和 Intel C++ 编译器 11.1.071 [IA-32] 的 visual studio 2008,这与我的其他相关 question

我正在尝试为 c++ 编写一个功能映射,它的工作方式有点像 ruby​​ 版本

strings = [2,4].map { |e| e.to_s }

所以我在 VlcFunctional 命名空间中定义了以下函数

template<typename Container, typename U>
vector<U> map(const Container& container, std::tr1::function<U(Container::value_type)> f)
{
    vector<U> transformedValues(container.size());
    int index = -1; 
    BOOST_FOREACH(const auto& element, container)
    {
        transformedValues.at(++index) = f(element);
    }
    return transformedValues; 
}

你可以这样调用它(注意函数模板参数是明确定义的):

vector<int> test;
test.push_back(2); test.push_back(4); 
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, [](int i) -> string
{
    return ToString(i);
});

或者像这样(注意函数模板参数没有明确定义)

std::tr1::function f = [](int i) -> string { return ToString(i); };
vector<string> mappedData2 = VlcFunctional::map<vector<int>,string>(test, f);

但重要的是,不要这样

vector<string> mappedData2 = VlcFunctional::map(test, [](int i) -> string { return ToString(i); });

如果没有 hte 模板参数的显式定义,它不知道使用哪个模板并因编译错误而失败

 ..\tests\VlcFunctional_test.cpp(106): error: no instance of function template "VlcFunctional::map" matches the argument list, argument types are: (std::vector<int, std::allocator<int>>, __lambda3)

必须定义模板参数使它的语法更加庞大,我的目标是在调用站点上尽量减少麻烦 - 关于为什么它不知道如何进行转换的任何想法?这是编译器问题还是语言不允许这种类型的模板参数推断?

最佳答案

问题是 lambda 不是 std::function,即使它可以转换。在推导类型参数时,不允许编译器对实际提供的参数执行转换。我会寻找一种方法让编译器检测类型 U 并让编译器免费推断第二个参数:

template <typename Container, typename Functor>
std::vector< XXX > VlcFunctional::map( Container &, Functor )...

现在的问题是在XXX中写什么。我没有和你一样的编译器,所有 C++0x 特性仍然有点棘手。我会首先尝试使用 decltype:

template <typename Container, typename Functor>
auto VlcFunctional::map( Container & c, Functor f ) -> std::vector< decltype(f(*c.begin())) > ...

或者,如果编译器还不支持 decltype,则可能是类型特征。

另请注意,您正在编写的代码在 C++ 中非常不惯用。通常在操作容器时,函数是根据迭代器实现的,你的整个映射基本上是旧的 std::transform:

std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<std::string> s;
std::transform( v.begin(), v.end(), std::back_inserter(s), [](int x) { return ToString(x); } );

std::transformmap 函数的 C++ 版本。虽然语法比较繁琐,但优点是你可以将它应用到任何容器,并产生输出到任何其他容器,所以转换容器不固定为 std::vector

编辑: 第三种方法,可能更容易在您当前的编译器支持下实现,是手动提供 lambda 的返回类型作为模板参数,并让编译器推断其余部分:

template <typename LambdaReturn, typename Container, typename Functor>
std::vector<LambdaReturn> map( Container const & c, Functor f )
{
   std::vector<LambdaReturn> ret;
   std::transform( c.begin(), c.end(), std::back_inserter(ret), f );
   return ret;
}
int main() {
   std::vector<int> v{ 1, 2, 3, 4, 5 };
   auto strs = map<std::string>( v, [](int x) {return ToString(x); });
}

即使您想为您的map 函数添加语法糖,当您可以使用现有功能时,也无需手动实现它。

关于c++ - 将 lambda 转换为 std::tr1::function,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3674648/

相关文章:

c++ - 将 for_each 与 std::unique_ptr 一起使用

c++ - 该程序不接受任何输入。第一次输入后自动关闭

c++ - GCC 5 及更高版本中的 AVX2 支持

haskell - 没有包装值(value)的单子(monad)?

python - Ruby 中的函数装饰器,与 Python 中一样

functional-programming - 寻找学习练习: implement these monads

c# - linq中的动态属性名称

c++ - 类设计建议 - C++

c++ - 在哪里更改引用代码以从 HEVC 编码视频中提取运动 vector

java - 需要访问多个方法才能比较对象