C++ 我可以让返回类型auto与签名相同但捕获不同的lambda一起工作吗?
我试图使用auto作为返回lambda函数的返回类型。下面是一个最简单的示例,演示了我在这方面遇到的一个问题:C++ 我可以让返回类型auto与签名相同但捕获不同的lambda一起工作吗?,c++,lambda,c++17,auto,C++,Lambda,C++17,Auto,我试图使用auto作为返回lambda函数的返回类型。下面是一个最简单的示例,演示了我在这方面遇到的一个问题: #include <iostream> #include <memory> auto get_func() { auto i = std::make_unique<int>(2); if (*i == 1) { return [i=std::move(i)]() { return *i;
#include <iostream>
#include <memory>
auto
get_func()
{
auto i = std::make_unique<int>(2);
if (*i == 1) {
return [i=std::move(i)]() {
return *i;
};
}
return [](){ return 2; };
}
int
main(int argc, char* argv[])
{
auto func = get_func();
std::cout << "val: " << func() << std::endl;
return 0;
}
对,它们都被推断为lambda
。它们都有相同的void(void)
签名。我看到问题是因为它们指定了不同的捕获吗?如果是这样,我有哪些选项可以让调用者使用相同的函数调用(如cout
callinmain
中所示)
它们都有相同的void(void)
签名
虽然lambda具有相同的运算符()
签名,但它们不是相同的类类型。它们是具有不同类型的不同对象。自动返回类型推断要求所有返回语句都具有相同的类型,这里没有
在这种情况下,如果您有不同的类型,您需要一个通用的返回类型。您可以使用std::function
获得它,因为它允许您通过操作符()而不是lambda类型返回。那就给你
std::function<void(void)> get_func()
{
auto i = std::make_unique<int>(2);
if (*i == 1) {
return [i=std::move(i)]() {
return *i;
};
}
return [](){ return 2; };
}
std::函数get_func()
{
自动i=std::使_唯一(2);
如果(*i==1){
返回[i=std::move(i)](){
返回*i;
};
}
return[](){return 2;};
}
我看到问题是因为它们指定了不同的捕获吗
即使它们完全相同,您也会看到一个问题,以此类推
每个lambda表达式创建一个唯一的闭包类型,它不同于任何其他lambda创建的任何闭包类型。无法协调此差异,因此auto
扣减无法成功
如果要返回两个不同的lambda(使用std::function
,或者使用支持仅移动语义的自定义类型),则需要键入擦除实际的functor。或者将整个逻辑滚动到一个lambda中:
auto
get_func()
{
auto i = std::make_unique<int>(2);
return [i=std::move(i)]() {
if (*i == 1) {
return *i;
}
return 2;
};
}
自动
get_func()
{
自动i=std::使_唯一(2);
返回[i=std::move(i)](){
如果(*i==1){
返回*i;
}
返回2;
};
}
我认为最好的变体(即双关语)是StoryTeller的解决方案,将逻辑放在一个lambda中
为了好玩,作为替代,您可以使用一个变体来保存lambda。您可以围绕它创建一个简单的包装:
template <class... Fs>
struct Lambda_fixed_variant
{
std::variant<Fs...> f_;
template <class L>
// requires std::is_constructible_v<decltype(f_), std::in_place_type_t<L>, L&&>
Lambda_fixed_variant(L l)
: f_{std::in_place_type_t<L>{}, std::move(l)}
{}
template <class... Args>
auto operator()(Args&&... args) const
{
return std::visit([&] (const auto& l) {
return l(std::forward<Args>(args)...);
},
f_
);
}
};
auto get_func()
{
auto i = std::make_unique<int>(2);
auto l1 = [i=std::move(i)]() { return *i; };
auto l2 = [](){ return 2; };
using L1 = decltype(l1);
using L2 = decltype(l2);
if (true)
{
return Lambda_fixed_variant<L1, L2>{std::move(l1)};
}
return Lambda_fixed_variant<L1, L2>{l2};
}
模板
结构Lambda_固定_变量
{
std::变体f_;
模板
//需要std::可构造吗
Lambda_固定_变型(L)
:f_{std::in_place_type_t{},std::move(l)}
{}
模板
自动运算符()(Args&&…Args)常量
{
返回标准::访问([&](const auto&l){
返回l(标准:正向(参数)…);
},
f_
);
}
};
自动获取函数()
{
自动i=std::使_唯一(2);
自动l1=[i=std::move(i)](){return*i;};
自动l2=[](){return 2;};
使用L1=decltype(L1);
使用L2=decltype(L2);
如果(真)
{
返回Lambda_固定_变量{std::move(l1)};
}
返回Lambda_固定_变量{l2};
}
那么我想我陷入了困境。您的示例没有编译:我不能使用std::function,因为我需要使用std::make_unique,这是由move捕获的。由于std::function需要复制功能(std::unique\u ptr不支持该功能),因此无法编译。@firebush D'oh。错过了,只是移动而已。是的,这行不通。您需要一个支持仅移动函子的版本,如:好的,这很有用,因为它会通知我必须使用的参数。我将使用shared_ptr,因为它是可复制的,并通过捕获将其移动到lambda中。然后我将按照您的建议使用std::函数。谢谢。@firebush没问题。很高兴能帮上忙。你能发布一个这个工作的代码示例吗?正如我对@NathanOliver提到的,捕获到的独特的\u ptr似乎排除了std::function.@firebush-这总是让我感到困惑。恐怕std::function
确实不行。这是标准库的僵局,需要自定义类型擦除函子。谢谢@storyteller。这证实了这一限制。相关问题
template <class... Fs>
struct Lambda_fixed_variant
{
std::variant<Fs...> f_;
template <class L>
// requires std::is_constructible_v<decltype(f_), std::in_place_type_t<L>, L&&>
Lambda_fixed_variant(L l)
: f_{std::in_place_type_t<L>{}, std::move(l)}
{}
template <class... Args>
auto operator()(Args&&... args) const
{
return std::visit([&] (const auto& l) {
return l(std::forward<Args>(args)...);
},
f_
);
}
};
auto get_func()
{
auto i = std::make_unique<int>(2);
auto l1 = [i=std::move(i)]() { return *i; };
auto l2 = [](){ return 2; };
using L1 = decltype(l1);
using L2 = decltype(l2);
if (true)
{
return Lambda_fixed_variant<L1, L2>{std::move(l1)};
}
return Lambda_fixed_variant<L1, L2>{l2};
}