返回没有std::function的lambda表达式

16

考虑以下返回lambda函数的函数:

std::function<int()> make_counter()
{
    int i = 0;
    return [=]() mutable { return i++; };
}

是否可能返回实际的lambda类型,而无需将其封装到std::function中?

3个回答

20
C++11:不行。每个lambda表达式都有“一个独特的、未命名的非联合类类型”,引用自§5.1.2/3。这实际上意味着,如果不知道相应的表达式,就无法知道lambda的类型。如果您没有捕获任何内容,您可以使用转换为函数指针并返回函数指针类型,但这非常有限。如@Luc在Lounge中所指出的,如果您愿意替换make_counter(如果它不是模板、重载或其他什么东西),则以下内容将起作用:
auto const make_counter = [](int i = 0) {
  return [i]() mutable { return i++; };
};

C++1y:是的,通过普通函数的返回类型推断(N3582)实现。



1
那个lambda表达式不是有效的C++11。如果主体包含除了return expression;之外的内容,返回类型推断将无法工作(§5.1.2,4)。 - Arne Mertz
从技术上讲,您可以返回“my_not_STD_function”,它只是重新实现了基本等效的东西... - Yakk - Adam Nevraumont
1
请注意,其中一个原因是 lambda 的类型不仅取决于其签名。每个捕获的变量(无论是通过复制还是引用)也需要考虑在内,而这些只有通过检查 lambda 主体才能知道。 - Matthieu M.

10

如果你作弊并使用返回类型推导,是的,你可以(Link)

请注意,这只在C++11之后才可能实现,但可以通过lambda表达式在常规的、不会产生警告的C++11中实现(即,在返回该lambda的lambda内部使用lambda表达式)。


不是C++11,也不是(目前)标准。 - Xeo
是的,C++ 仍然很慢。=[ - user1357649
@Fred 再次查看链接,并注意用于编译的命令行。 - R. Martinho Fernandes
“Z12make_countervEUlvE_”类型的对应C++表达式是什么?我能否像旧的c++98一样定义lambda,而不使用std::function和auto?如果答案是肯定的,那么如何实现? - Liu Weibo

0

是的,你可以通过一些受这篇博客文章启发的lambda技巧来实现这个。

第一步是提供一个帮助器,使lambda成为字面类型:

template<class F>
struct literal_lambda_t {
    static_assert(std::is_empty<F>(), "Lambdas must be empty (have no captures)");

    template<typename... Args>
    auto operator()(Args&&... args) const -> decltype(
        std::declval<F const&>()(std::forward<Args>(args)...)
    ) {
        // since the object is empty, reinterpreting it should be safe
        return reinterpret_cast<const F&>(*this)(std::forward<Args>(args)...);
    }
};

template<class F>
constexpr literal_lambda_t<typename std::remove_reference<F>::type> literal_lambda(F &&f) {
    return {};
}

现在您可以声明一个“私有”函数,利用Lambda具有返回类型推断的特性:
namespace detail {
    static constexpr auto make_counter = literal_lambda([](){
        int i = 0;
        return [=]() mutable { return i++; };
    });
}

最后,您可以使用这个私有 lambda 来声明您的真实函数:

auto make_counter() -> decltype(detail::make_counter()) {
    return detail::make_counter();
}

这种方法甚至适用于模板函数:
namespace detail {
    template<typename T>
    struct typed_counter_helper {
        // can't template this, so have to template an enclosing struct
        static constexpr auto f = literal_lambda([](){
            T i = 0;
            return [=]() mutable { return i++; };
        });
    };
}
template<typename T>
auto make_typed_counter() -> decltype(typed_counter_helper<T>::f()) {
    return typed_counter_helper<T>::f::make_counter();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接