【问题标题】:Generic lambda with trailing return type depending on variadic arguments in C++11 [duplicate]具有尾随返回类型的通用 lambda 取决于 C++11 中的可变参数 [重复]
【发布时间】:2017-10-17 06:29:44
【问题描述】:

在 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 中,由于我们无法在类中存储可变数量的成员,因此我不知道如何才能获得完全相同的结果。有什么想法吗?

【问题讨论】:

  • 我不...明白你的问题是什么。你到底想做什么?您最初的 C++14 示例和 C++11 示例不等效,因此我无法推断问题所在。
  • 它们怎么不相等?我在 C++11 中看不到任何其他方法可以达到相同的结果...
  • 至于我的用法,我使用带有 SFINAE 的 lambda 来检测 constexpr 输入(显然没有使用 lambda)。它在 C++14 中运行良好,但我试图使其与 C++11 兼容。有关类似用例,请参阅 stackoverflow.com/a/40413051/2950563
  • lambda 示例没有捕获任何内容。在Functor 示例中(实际上不是functor),它有一个成员变量。无论如何,如果它有一个明确的问题陈述说明您可以在 C++14 中做什么但在 C++11 中遇到困难,它将对您的问题有很大帮助。
  • T 替换为std::tuple&lt;Ts...&gt;。借助 std::index_sequence 的 c++11 等效项将元组解包到参数列表中

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


【解决方案1】:

据我了解,可以从 @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

【讨论】:

  • 对于std::apply() 的 C++14 实现,另请参阅我的回答 here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-05
  • 2020-01-24
  • 2011-04-14
  • 2020-09-13
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
相关资源
最近更新 更多