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

时间:2017-05-17 16:58:31

标签: c++ c++11 lambda c++14

在C ++ 14中,您可以执行以下操作:

require 'set'

a = ['a', 'b', 'c']
set = Set.new(['b', 'c'])
a.index { |x| set.include?(x) }
#=> 1

为了举例,考虑Placeholder :: fct实际上是将输入类型操作为其他东西(在本例中,函数调用是无用的)。

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

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, "");
}

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

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, "");
}

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

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

但是,在C ++ 11中,由于我们无法在类中存储可变数量的成员,因此我看不出如何实现完全相同的结果。有什么想法吗?

1 个答案:

答案 0 :(得分:2)

根据我的理解,可以从@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>());
}

现在,我们可以通过在您的班级中存储元组并使用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