线性链式工厂和移动语义

时间:2018-05-15 16:58:08

标签: c++ templates factory move-semantics expression-templates

我试图建造连锁工厂。

链将使用移动语义来避免构造不必要的对象。

链接遵循3条规则:

  1. 除非工厂现在被标记为制造,否则两家工厂可以表达:

    • A_maker ^ B_maker<...>成为Exp<A_maker, B_maker>
  2. 除非工厂现在被标记为生产,否则表达式和工厂会生成一个新表达式:

    • Exp<...> ^ B_maker<...>成为Exp<Exp<...>, B_maker<...>>
  3. A_maker(..) ^ (B_maker(..).make())的语法会创建B<A>个对象。

  4. 我没有实现制作B<B<A>>的逻辑,或者将需求从后来的工厂传递回早期的工厂。虽然对象的创建遵循工厂的顺序,但要求与此顺序相反。

    当前代码的问题在于它无法编译。

    如何解决这个问题?

    感谢。

    测试(也在coliru

    #include <iostream>
    #include <utility>
    
    struct A {
        explicit A(int val) : val_(val) {}
    
        int val_{-1};
    };
    
    template<typename P=A>
    struct B {
        B(P&& p, int val)
                : p_(p), val_(val) {}
    
        P p_;
        int val_{-1};
    };
    
    template<typename Top, typename Rest>
    class Exp {
    public:
        Exp(Top&& top, Rest&& rest)
                : top_(std::move(top)),
                  rest_(std::move(rest)) {}
    
        template<typename Maker>
        auto operator^(const Maker& m) {
            if (m.make_now_) {
                return m.syn(make());
            } else {
                return append(m);
            }
        }
    
        auto make() { return rest_.syn(top_.syn); }
    
    private:
        template<typename New_maker>
        Exp<Top, Exp<Rest, New_maker>> append(
                New_maker&& new_maker) {
            return Exp(top_, Exp(rest_, new_maker));
        }
    
        Top top_;
        Rest rest_;
    };
    
    class A_maker {
    public:
        explicit A_maker(int val) : val_(val) {}
    
        auto make() { return syn(); }
    
        template<typename T>
        auto operator^(T&& other_maker) {
            return Exp<A_maker, T>(std::move(*this),
                                   other_maker);
        }
    
    private:
        A syn() { return A(val_); }
    
        int val_;
    
        template<typename T, typename R> friend
        class Exp;
    };
    
    template<typename P=A>
    class B_maker {
        using self_type = B_maker<P>;
    public:
    
        explicit B_maker(int val) : val_(val) {}
    
        self_type&& make() {
            make_now_ = true;
            return std::move(*this);
        }
    
    private:
        B<P> syn(P&& p) { return B(p, val_); }
    
        bool make_now_{false};
        int val_;
    
        template<typename T, typename R> friend
        class Exp;
    };
    
    int main() {
        B bba(B(A(0), 1), 2);
        auto x = A_maker(0) ^B_maker(1).make();
        return 0;
    }
    

    编译错误:

    error: cannot bind rvalue reference of type 
        ‘B_maker<A>&&’ to lvalue of type ‘B_maker<A>’
             return Exp<A_maker, T>(std::move(*this), other_maker);
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

1 个答案:

答案 0 :(得分:2)

您必须使用std::forwardhttps://godbolt.org/g/VfNk2G

你在函数

template<typename T>
auto operator^(T&& other_maker) {
    return Exp<A_maker, T>(std::move(*this),
                           other_maker);
}

您正试图致电Exp::Exp(Top&& top, Rest&& rest)并且编译器抱怨rest(即other_maker)的类型为B_maker<A>,即使您的函数需要B_maker<A>&& other_maker 。这里的问题是,只要您命名该值,它就会突然变为左值而不再是B_maker<A>&&类型。 std::forward<T&&>通过再次将其作为右值引用来修复此问题。