auto dothings = [](long position) {
auto variable;
/*do things*/
return variable;
};
float x = dothings(1l);
char y = dothings(2l);
基本上,我很好奇的是,lambda 内部的变量是否有可能以任何方式推断返回值分配给的类型,在这种情况下它是 float 和 char。有没有等同于模板类型名的东西?谢谢。
最佳答案
这可以做到,但它 a) 有点复杂,b),在 99.9% 的情况下,这并不是一个好主意。以下是您的操作方式。您可以根据将表达式分配给的类型执行某些操作的唯一方法是利用隐式转换。这需要一个模板化的隐式对话运算符,它不能在 lambda 中本地声明,所以我们必须从编写一些支持代码开始:
template <class T>
struct identity {
T get(); // not defined
};
template <class F>
struct ReturnConverter {
F f;
template <class T>
operator T() {
return f(identity<T>{});
}
};
template <class F>
auto makeReturnConverter(F f) { return ReturnConverter<F>{f}; }
第一类只是帮助 lambda 推断类型。第二类本身采用 lambda(或任何可调用),并且具有到任何类型的隐式转换运算符。当请求转换时,它会调用可调用对象,使用我们的 identity
类模板作为提供类型的一种方式。我们现在可以像这样使用它:
auto doIt = [] (long l) {
return makeReturnConverter([=] (auto t) {
return l + sizeof(decltype(t.get()));
});
};
这个 lambda 通过输入另一个 lambda 创建了我们特殊的 ReturnConverter
类。此 lambda 捕获外部 lambda 的 long l
参数(按值),并准备接受我们的特殊身份类作为唯一参数。然后它可以取消“目标”或目标类型。我在这里使用 sizeof
来快速显示一个示例,其中结果取决于 lambda 的参数和目标类型。但请注意,一旦我使用 decltype(t.get())
获得类型,我实际上可以声明该类型的变量并用它做任何我想做的事。
float x = doIt(5);
double y = doIt(2);
在这些调用之后,x
将为 9( float 的大小为 4 + 5)并且 y
将为 10(double 的大小为 8 + 2)。
这是一个更有趣的例子:
auto doIt2 = [] (long l) {
return makeReturnConverter([=] (auto t) {
decltype(t.get()) x;
for (std::size_t i = 0; i != l; ++i ) {
x.push_back(i*i);
}
return x;
});
};
std::vector<int> x = doIt2(5);
std::deque<int> y = doIt2(5);
在这里,只要标准容器具有 push_back
方法,我就可以根据左侧的要求构建标准容器。
就像我开始说的那样,这在绝大多数情况下都过于复杂且难以证明。通常你可以只将特定类型作为显式(非推断)模板参数和 auto
左边。不过,我在非常具体的情况下使用过它。例如,在 gtest 中,当您声明测试夹具时,任何重复使用的数据都被声明为夹具中的成员变量。非静态成员变量必须用它们的类型声明(不能使用 auto),所以我使用这个技巧来允许快速构建某些类型的夹具数据,同时尽可能将重复保持为接近零。在这个用例中没问题,因为该代码不需要非常健壮,但确实想不惜一切代价尽量减少重复;通常这不是一个很好的权衡(并且通常不需要在左侧提供 auto
)。
关于c++ - Lambda 类型推导,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52067781/