【问题标题】:Perfect forwarding to a lambda received as an argument完美转发到作为参数接收的 lambda
【发布时间】:2019-05-21 22:05:31
【问题描述】:

class Frame<P> 表示像素类型为P 的图像。由于底层数据缓冲区格式具有多种灵活性,迭代其像素的算法并非易事。因此,我想避免在Frame<P>::iterate 中重复代码。

template<typename P>
class Frame {
    std::vector<P> buf;
public:
    template<typename F>
    void iterate(F f) const {
        // iterate in a way that is performant for this buffer
        // but here i use a simple iteration for demonstration
        for(const P& p : buf){
            for(int i=0; i<buf.size(); i++){
                f(buf.data()[p]);
            }
        }
    }
}

这允许(在const Frame&lt;P&gt;&amp; frame 上):

  • frame.iterate([](const P&amp; p){ /* ... */ });

但我也想支持(在非常量 Frame&lt;P&gt;&amp; frame 上):

  • frame.iterate([](P&amp; p){ /* ... */ });
  • std::move(frame).iterate([](P&amp;&amp; p){ /* ... */ });

有没有一种简单的方法可以做到这一点而无需复制iterate 中的代码?

【问题讨论】:

  • 不过,还不止这些。您还需要从&amp;&amp; 限定函数中的buf 成员中获取std::move_iterator,否则您需要for (auto&amp;&amp; p : buf) { f(forward_like&lt;Self&gt;(p)); } 的某种forward_like&lt;Self&gt;(p) 函数才能实际移动p 元素.
  • @Justin 我已经编辑了实现(同时保持最小化)以反映它实际上是在使用指针偏移进行迭代。您认为可以在不重复我编辑的实现的情况下完成吗?我什至不知道成员函数可以是“ref-qualified”。
  • 你的新实现意味着什么? P 类型用于索引缓冲区本身,而 i 完全未使用?您的意思是完全移除外循环吗?

标签: c++ lambda c++14 rvalue-reference perfect-forwarding


【解决方案1】:

这与How do I remove code duplication between similar ref-qualified member functions? 有关,但还需要做更多工作。简而言之,我们希望将iterate 的引用限定版本转发到静态成员函数,该函数可以适当地转发buf 的各个元素。这是一种方法:

template <typename P>
class Frame {
    std::vector<P> buf;

    template <typename F, typename Iter>
    static void iterate_impl(F&& f, Iter first, Iter last) {
        while (first != last) {
            f(*first++);
        }
    }

public:
    template <typename F>
    void iterate(F f) const& {
        iterate_impl(f, buf.begin(), buf.end());
    }

    template <typename F>
    void iterate(F f) & {
        iterate_impl(f, buf.begin(), buf.end());
    }

    template <typename F>
    void iterate(F f) && {
        iterate_impl(f,
            std::make_move_iterator(buf.begin()),
            std::make_move_iterator(buf.end()));
    }

    template <typename F>
    void iterate(F f) const&& {
        iterate_impl(f,
            std::make_move_iterator(buf.begin()),
            std::make_move_iterator(buf.end()));
    }
};

另一种方法是使用forward_like 函数:

template <typename T, typename U>
struct copy_cv_ref {
private:
    using u_type = std::remove_cv_t<std::remove_reference_t<U>>;
    using t_type_with_cv = std::remove_reference_t<T>;

    template <bool condition, template <typename> class Q, typename V>
    using apply_qualifier_if = std::conditional_t<condition,
        Q<V>,
        V
    >;

    static constexpr bool is_lvalue = std::is_lvalue_reference<T>::value;
    static constexpr bool is_rvalue = std::is_rvalue_reference<T>::value;
    static constexpr bool is_const = std::is_const<t_type_with_cv>::value;
    static constexpr bool is_volatile = std::is_volatile<t_type_with_cv>::value;

public:
    using type =
        apply_qualifier_if<is_lvalue, std::add_lvalue_reference_t, 
        apply_qualifier_if<is_rvalue, std::add_rvalue_reference_t,
        apply_qualifier_if<is_volatile, std::add_volatile_t,
        apply_qualifier_if<is_const, std::add_const_t, u_type
    >>>>;
};

template <typename T, typename U>
using copy_cvref_t = typename copy_cv_ref<T, U>::type;

template <typename Like, typename U>
constexpr decltype(auto) forward_like(U&& it) {
    return static_cast<copy_cvref_t<Like&&, U&&>>(std::forward<U>(it));
}

然后你可以在iterate_impl的实现中使用它:

template <typename P>
class Frame {
    std::vector<P> buf;

    template <typename Self, typename F>
    static void iterate_impl(Self&& self, F&& f) {
        for (int i = 0; i < self.buf.size(); ++i) {
            f(forward_like<Self>(self.buf[i]));
        }
    }

public:
    template <typename F>
    void iterate(F f) const& {
        iterate_impl(*this, f);
    }

    template <typename F>
    void iterate(F f) & {
        iterate_impl(*this, f);
    }

    template <typename F>
    void iterate(F f) && {
        iterate_impl(std::move(*this), f);
    }

    template <typename F>
    void iterate(F f) const&& {
        iterate_impl(std::move(*this), f);
    }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 2012-11-28
    • 2017-08-05
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多