具有尾随 return 类型的通用 lambda 取决于 C++11 中的可变参数

Generic lambda with trailing return type depending on variadic arguments in C++11

在 C++14 中,您可以这样做:

struct Placeholder
{
    template<typename T>
    constexpr static T fct(T val) { return val; }
};

int main()
{
    constexpr int i{};
    auto fct = [](auto&& placeholder) -> decltype(placeholder.fct(i)) { return 5.5f; };
    static_assert(fct(Placeholder{}) == 5, "");
}

为了示例,请考虑 Placeholder::fct 实际上是将输入类型操纵为其他类型(在当前情况下,函数调用是无用的)。

另一方面,在 C++11 中,您可以通过声明模板仿函数来模拟通用 lambda。事实上,我可以简单地将 i 传递给构造函数并将其存储为成员,如下所示:

template<typename T>
class Functor
{
    T i;
public:
    constexpr Functor(T i) : i{ i } {}
    template<typename P>
    constexpr auto operator()(P&& placeholder) const -> decltype(placeholder.fct(i))
    {
        return 5.5f;
    }
};

int main()
{
    constexpr int i{};
    constexpr Functor<decltype(i)> fct(i);
    static_assert(fct(Placeholder{}) == 5, "");
}

当我们希望占位符采用可变数量的参数时,问题就来了,如下所示:

struct Placeholder
{
    template<typename... T>
    constexpr static auto fct(T... val) -> typename std::common_type<T...>::type
    {
        return { /* Do something with the values */ };
    }
};

事实上,在 C++14 中,我们可以简单地将值直接传递给 lambda:

decltype(placeholder.fct(1, 2, 3))

但是,在 C++11 中,由于我们无法在 class 中存储可变数量的成员,我不知道如何才能获得完全相同的结果。有什么想法吗?

根据我的理解,可以从@RichardHodges 的想法中得出一个纯 C++11 解决方案。您需要手动重新编码 std::apply。为此,您还需要重新编码 std::integer_sequencestd::index_sequencestd::make_index_sequence。让我们从那个开始:

template <typename T, T... Is>
struct integral_sequence {};

template <std::size_t... Is>
using index_sequence = integral_sequence<std::size_t, Is...>;

template <typename Seq, typename T, T... el>
struct append_sequence;

template <typename T, T... el, T... Is>
struct append_sequence<integral_sequence<T, Is...>, T, el...> {
    using type = integral_sequence<T, Is..., el...>;
};

namespace details {

template <std::size_t N>
struct make_index_sequence_impl {
private:
    using seq = typename make_index_sequence_impl<N-1>::type;
public:
    using type = typename append_sequence<seq, std::size_t, N>::type;
};

template <>
struct make_index_sequence_impl<0u> {
    using type = index_sequence<0>;
};

template <std::size_t N>
struct make_index_sequence {
    using type = typename make_index_sequence_impl<N-1>::type;
};

template <>
struct make_index_sequence<0u> {
    using type = index_sequence<>;
};

} // namespace details

template <std::size_t N>
using make_index_sequence = typename details::make_index_sequence<N>::type;

现在,我们可以处理 apply 实施。它的目标是将 tuple 作为输入并将其内容解压转发。例如,apply([](int x, int y) { /* impl */ }, std::make_tuple(0, 2)) 等同于 [](int x, int y) { /* ... */ }(0, 2)

为此,我们首先需要使用 index_sequence:

将元组内容分派给函子
namespace details {

template <typename F, typename Tuple, std::size_t... Is>
auto apply_impl(F&& ftor, Tuple&& tuple, index_sequence<Is...>) -> decltype(std::forward<F>(ftor)(std::get<Is>(tuple)...)) {
    return std::forward<F>(ftor)(std::get<Is>(tuple)...);
}

} // namespace details

然后,暴露的apply进来了:

template <typename F, typename Tuple>
template <typename F, typename Tuple>
auto apply(F&& ftor, Tuple&& tuple) -> decltype(details::apply_impl(std::forward<F>(ftor), std::forward<Tuple>(tuple), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type>::value>())){
    return details::apply_impl(std::forward<F>(ftor), std::forward<Tuple>(tuple), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type>::value>());
}

现在我们可以通过在 class 中存储一个元组并使用 apply 将其内容分派给 placeholder 仿函数来获得您想要的行为:

template <typename... Ts>
class Functor {
    std::tuple<Ts...> is;
public:
    constexpr Functor(Ts... ts) : is(std::make_tuple(ts...)) {}
    template <typename P>
    constexpr auto operator()(P&& placeholder) -> decltype(apply(std::forward<P>(placeholder), is)) {
        return apply(std::forward<P>(placeholder), is);
    }
};

将所有内容与一些示例放在一起得出 Live Demo