我有两个相关的问题,都与如何统一处理左值和左值引用有关。
struct Foo {
Object m_object;
virtual void bar(SomeRefTypeOfObject object);
};
基本上,我想要实现的是SomeRefTypeOfObject
,它能够存储Object
的左值和左值引用。 bar
是一个更大的函数,它将使用一个m_object = object;
语句存储object
的值(复制或移动操作,具体取决于存储的引用的类型)。原因是我想避免使用两个bar
函数(对于每种引用类型)。标准库中是否有任何可以方便有效地执行此操作的内容,或者我是否必须为此推出自己的解决方案?如果我必须推出自己的解决方案,那么良好的实现将如何?
struct Foo {
Object m_object;
template <...>
void bar(... object);
};
我希望拥有一个bar
模板,可以使用任何类型的Object
或其派生类/其他可以转换为Object
的对象进行调用(例如bar
是两个带有const Object &
和Object &&
参数的重载函数,但是仅使用 const Object &
和Object &&
进行实例化。所以,我不希望为每个派生类型实例化bar
。最明确的方法是什么?我想我在这里需要某种形式的SFINAE。
注意:m_object = object;
赋值可以在bar
调用的函数中进行。因此,按值传递对象的解决方案不是最佳的,因为将进行不必要的复制/移动(可能存在移动并不像传递引用那样便宜的类型)。此外,赋值可以以条件方式发生,因此只在叶子函数中传递对象而不是解决方案(因为它可以依赖于运行时,哪个函数执行实际赋值)。
答案 0 :(得分:1)
类型擦除(或者可能是参考擦除可能是更好的术语)方法草图:
#include <iostream>
#include <type_traits>
#include <utility>
struct Object {
Object() {
std::cout << "Object()" << std::endl;
}
Object(const Object &) {
std::cout << "Object(const Object &)" << std::endl;
}
Object(Object &&) {
std::cout << "Object(Object &&)" << std::endl;
}
Object &operator =(const Object &) {
std::cout << "Object &operator =(const Object &)" << std::endl;
return *this;
}
Object &operator =(Object &&) {
std::cout << "Object &operator =(Object &&)" << std::endl;
return *this;
}
};
template <class T>
struct RLReferenceWrapper {
bool is_rvalue;
T *pnull;
const T& cref;
T&& rref;
RLReferenceWrapper(T&& rref): is_rvalue(true), pnull(nullptr), cref(*pnull), rref(std::move(rref)) { }
RLReferenceWrapper(const T& cref): is_rvalue(false), pnull(nullptr), cref(cref), rref(std::move(*pnull)) { }
template <class L>
void Visit(L l) {
if (is_rvalue) {
l(std::move(rref));
} else {
l(cref);
}
}
};
struct Foo {
Object m_object;
virtual void bar(RLReferenceWrapper<Object> wrapper) {
wrapper.Visit([this](auto&& ref){
m_object = std::forward<std::remove_reference_t<decltype(ref)>>(ref);
});
}
};
int main() {
Foo f;
f.bar(Object{});
Object o2;
f.bar(o2);
}
输出:
Object()
Object()
Object &operator =(Object &&)
Object()
Object &operator =(const Object &)
答案 1 :(得分:0)
替代解决方案可能(未经测试):
template <class T> struct movable{};
template <class T> struct movable<T const&>
{
movable( movable const& ) = delete;
movable( T const& r ): ref_{ const_cast<T&>(r) }, movable_{false} {}
movable( T&& r ): ref_{r}, movable_{true} {}
auto get() const -> T const& { return ref_; }
auto move() -> T&& { if( !movable_ ) throw std::runtime_error{"bad move!"}; movable_ = false; return std::move(ref_); }
template <class V>
void assign_or_move_to( V& v ) { if( movable_ ) v = move(); else v = get(); }
private:
T& ref_;
bool movable_;
};
struct Foo {
Object m_object;
virtual void bar( movable<Object const&> object_ref )
{
auto& object = object_ref.get();
// ...
object_ref.assign_or_move_to( m_object );
// or
if( something ) m_object = object_ref.move();
}
};
那说,我怀疑它是值得的,你还是应该解释一下为什么你拒绝写两个条重载开始......
答案 2 :(得分:0)
怎么样
struct Foo {
Object m_object;
void bar(Object& o) { bar_impl(o); }
void bar(Object&& o) { bar_impl(std::move(o)); }
template <typename T>
void bar_impl(T&& object) {
// snip ...
m_object = std::forward<T>(object);
}
};
我想这个:
它没有包装对象或其他任何东西,但我认为好的b / c T&&
不是这样的右值,它是这里解释的通用引用:https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers(谢谢斯科特!)