C++ 我可以让返回类型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;

我试图使用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;
        };
    }
    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
callin
main
中所示)

它们都有相同的
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};
}