C ++中是否有任何技巧可以确保类的用户只能生成rvalues?
示例:
struct PoorClass { /* ... */};
struct EnrichedClass {
explicit EnrichedClass (const PoorClass & poor)
: m_poor (poor)
{
}
/* additional functionality for poor objects */
private:
const PoorClass & m_poor;
}
const EnrichedClass asEnriched (const PoorClass & poor)
{
return EnrichedClass { poor };
}
现在,丰富的物品应该只是临时物品,因为它们不应该在被包裹的不良物体中存活。理想情况下,富集对象不应存储在变量中,而只能作为对函数的右值引用传递。
有没有办法确保尽可能保证安全?
答案 0 :(得分:4)
您只能为struct EnrichedClass
提供r值方法:
struct EnrichedClass {
explicit EnrichedClass (const PoorClass& poor) : m_poor (poor) {}
/* additional functionality for poor objects */
void foo() &&; // note the && at the end.
private:
const PoorClass & m_poor;
};
答案 1 :(得分:4)
Afaik,没有办法直接达到预期的任务。
我可以想办法以某种方式解决问题':
使用命名的静态构造函数创建一个私有或受保护的构造函数和复制构造函数。
有些事情:
struct PoorClass { /* ... */ };
struct EnrichedClass
{
EnrichedClass& operator= (const EnrichedClass & enr) = delete;
private:
explicit EnrichedClass(const PoorClass & poor)
: m_poor(poor)
{ }
EnrichedClass(const EnrichedClass & enr)
: m_poor(enr.m_poor)
{ }
public:
static EnrichedClass enrich(const PoorClass & poor)
{
return EnrichedClass{ poor };
}
void stuff() { }
private:
const PoorClass & m_poor;
};
因此,无法复制实例:
EnrichedClass::enrich(p).stuff(); // works
auto ec = EnrichedClass::enrich(p); // does not
注意:通过rvalue-references,可能仍然有EnrichedClass
的左值。
void foo(EnrichedClass && e)
{
// e = lvalue here
}
关于rvalue-ref资格认证:
void bar() && { /* do stuff */ }
如果你想使用rvalue-references将rvalue传递给一个函数,你不能用&&
ref资格调用成员函数,因为rvalue-references仍然是lvalues(即e
不是foo
里面的rvalue上面{1}}。
你可以这样做:
void foo(EnrichedClass && e)
{
std::move(e).bar();
}
但现在您已为bar
提供了保证:this
引用的对象是右值。只要对象在调用后处于有效(但未指定)状态(即bar
可以从this
移动),就可以随心所欲地做任何事情。
因此,在致电bar
后,您可能不知道e
的状态。
答案 2 :(得分:3)
看起来你可以用:
来做到这一点asEnriched(PoorClass)
返回EnrichedClass&& 以下是一个例子:
#include <iostream>
struct PoorClass {
// trace construction and destruction
PoorClass() {
std::cerr << "Create " << this << std::endl;
}
~PoorClass() {
std::cerr << "Destroy " << this << std::endl;
}
};
struct EnrichedClass {
private:
EnrichedClass (const PoorClass & poor)
: m_poor (poor)
{
}
EnrichedClass(const EnrichedClass& rich): m_poor(rich.m_poor){}
/* additional functionality for poor objects */
const PoorClass & m_poor;
friend const EnrichedClass&& asEnriched (const PoorClass & poor);
friend void dump(const EnrichedClass& rich); // only to be able to access m_poor
};
const EnrichedClass&& asEnriched (const PoorClass & poor)
{
return EnrichedClass ( poor ); // gives a warning, but seems harmless
// provided the ref in only used as a parameter to a function call
// of course don't affect it to a true ref, or do not take the address
// if you do not want dangling refs or pointers !
}
void dump(const EnrichedClass & rich) {
std::cerr << "Dump " << &(rich.m_poor) << std::endl;
}
int main() {
PoorClass poor;
// EnrichedClass rich = poor; causes an error
// EnrichedClass rich = asEnriched(poor); causes an e};
dump(asEnriched(PoorClass()));
std::cerr << "End" << std::endl;
return 0;
}
显示:
Create 0xbfbfec58
Create 0xbfbfec50
Dump 0xbfbfec50
Destroy 0xbfbfec50
end
Destroy 0xbfbfec58
意味着引用在转储调用的持续时间内有效,而不可能影响EnrichedClass
到变量。
当然,您可能会影响参考。例如,const EnrichedClass& ref = asEnriched(poor);
将被接受,但会给你一个悬空参考。
我无法想象一种强制编译器将其检测为错误的方法 - 毕竟它已经警告asEnriched
返回对临时的引用...
答案 3 :(得分:1)
不确定问题,不确定我的答案。
但我认为你可以通过隐藏Poor
Enriched
并重载Poor
运算符来实现这一目标:
class Better{
class Poor{};
Poor& m_Poor;
public:
Better(class Poor& poor) :m_Poor(poor){}
operator Poor(){
return m_Poor;
}
};
你不能直接创造穷人,因为它是私人的。您只能提供一些r值并将Better
作为Poor
传递,因为它有转换运算符。