使用带有 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::transform
是 map
函数的 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/