代码之家  ›  专栏  ›  技术社区  ›  Museful

完美转发到作为参数接收的lambda

  •  1
  • Museful  · 技术社区  · 5 年前

    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<P>& frame ):

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

    但我也要支持 Frame<P>& frame ):

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

    有没有一个简单的方法不用复制 iterate ?

    0 回复  |  直到 5 年前
        1
  •  1
  •   Justin    5 年前

    这与 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);
        }
    };