Линейно-связанные фабрики и семантика перемещения - PullRequest
0 голосов
/ 15 мая 2018

Я пытаюсь сделать скованные фабрики.

Цепочка будет использовать семантику перемещения, чтобы избежать создания ненужных объектов.

Цепочка подчиняется 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> объект.

Я не реализовал логику для создания B<B<A>> или передачи требований от более поздней фабрики обратно к более ранней фабрике. Хотя создание объектов следует за порядком заводов, требования передаются противоположно этому порядку.

Проблема с текущим кодом в том, что он не компилируется.

Как это исправить?

Спасибо.

Тест (также при колиру )

#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 голосов
/ 15 мая 2018

Вы должны использовать std::forward: https://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. Проблема здесь в том, что как только вы назовете это значение, оно снова становится lvalue и больше не имеет типа B_maker<A>&&. std::forward<T&&> исправляет это, снова делая ссылку на rvalue.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...