I try to make chained factories.
The chain would use move semantics to avoid constructing unnecessary objects.
The chaining follows 3 rules:
Unless a factory is flagged to make now, two factories can make an expression:
A_maker ^ B_maker<...>
becomes anExp<A_maker, B_maker>
Unless a factory is flagged to make now, an expression and a factory makes a new expression:
Exp<...> ^ B_maker<...>
becomes anExp<Exp<...>, B_maker<...>>
.
A syntax of
A_maker(..) ^ (B_maker(..).make())
would create aB<A>
object.
I haven't implemented the logic to make B<B<A>>
, or passing requirements from a later factory back to earlier factories. While creation of objects follows a the ordering of the factories, requirements are passed opposite to this ordering.
The problem with the current code is that it doesn't compile.
How to fix this?
Thanks.
Test (also at 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;
}
Compilation Error:
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);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You must use
std::forward
: https://godbolt.org/g/VfNk2GYou are in the function
You are trying to call
Exp::Exp(Top&& top, Rest&& rest)
and the compiler complains thatrest
(i.e.other_maker
) is of typeB_maker<A>
even though your function takes aB_maker<A>&& other_maker
. The problem here is that as soon as you name that value, it is suddenly an lvalue again and no longer of typeB_maker<A>&&
.std::forward<T&&>
fixes that by making it an rvalue reference again.