线性链式工厂和移动语义
Linearly Chained Factories and Move Semantics
我尝试制作链式工厂。
链将使用移动语义来避免构造不必要的对象。
链接遵循 3 条规则:
除非一个工厂被标记为现在制造,否则两个工厂可以制造一个表达式:
A_maker ^ B_maker<...>
变成了 Exp<A_maker, B_maker>
除非一个工厂被标记为现在制造一个表达式并且工厂制造一个新的表达式:
Exp<...> ^ B_maker<...>
变成了 Exp<Exp<...>, B_maker<...>>
.
A_maker(..) ^ (B_maker(..).make())
的语法将创建一个 B<A>
对象。
我还没有实现制作 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);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
您必须使用 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>
类型,即使您的函数采用 [=16] =].这里的问题是,一旦你 name 这个值,它突然又是一个左值并且不再是 B_maker<A>&&
类型。 std::forward<T&&>
通过再次将其设为右值引用来解决此问题。
我尝试制作链式工厂。
链将使用移动语义来避免构造不必要的对象。
链接遵循 3 条规则:
除非一个工厂被标记为现在制造,否则两个工厂可以制造一个表达式:
A_maker ^ B_maker<...>
变成了Exp<A_maker, B_maker>
除非一个工厂被标记为现在制造一个表达式并且工厂制造一个新的表达式:
Exp<...> ^ B_maker<...>
变成了Exp<Exp<...>, B_maker<...>>
.
A_maker(..) ^ (B_maker(..).make())
的语法将创建一个B<A>
对象。
我还没有实现制作 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);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
您必须使用 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>
类型,即使您的函数采用 [=16] =].这里的问题是,一旦你 name 这个值,它突然又是一个左值并且不再是 B_maker<A>&&
类型。 std::forward<T&&>
通过再次将其设为右值引用来解决此问题。