左值引用类型成员的用户定义移动构造函数
User-defined move constructor for member of lvalue reference type
我正在编译器上玩移动语义,该编译器具有右值引用,但不支持默认的移动构造函数。我想生成类似于下面的包装类的东西,即使模板参数是左值引用,它也可以工作。但是,这种简单的方法无法编译,因为它尝试从 int 初始化 int&。
#define USER_DEFINED 0
template <typename T>
struct Wrapper
{
Wrapper(T t)
: m_t(t)
{
}
Wrapper(const Wrapper&) = delete;
Wrapper& operator=(const Wrapper&) = delete;
#if USER_DEFINED
Wrapper(Wrapper&& w)
: m_t(std::move(w.m_t))
{
}
#else
Wrapper(Wrapper&&) = default;
#endif
private:
T m_t;
};
int main()
{
int i = 0;
Wrapper<int&> w1 = Wrapper<int&>(i);
Wrapper<std::string> w2 = Wrapper<std::string>("text");
}
显而易见的解决方案是有两个移动构造函数,一个用于左值引用,一个用于所有其他类型。例如,像这样的东西:
template <typename U = T>
Wrapper(typename std::enable_if<!std::is_lvalue_reference<U>::value, Wrapper>::type&& w)
: m_t(std::move(w.m_t))
{
}
template <typename U = T>
Wrapper(typename std::enable_if<std::is_lvalue_reference<U>::value, Wrapper>::type&& w)
: m_t(w.m_t)
{
}
那么这是要走的路吗?也许enable_if<>
内的表达式应该更通用?或者我可以使用与 std::move() 不同的东西,并为所有类型的一个构造函数使用一个构造函数?
好的,这是一个我认为可以按照您想要的方式工作的解决方案,但我必须承认我不完全了解它是如何做到的。
我所做的最重要的更改是在移动构造函数中将std::move
替换为std::forward<T>
。 我还添加了一个移动分配运算符,但这是我不明白的事情:除非在其他地方,否则它需要std::move
而不是std::forward
! 最后,我还向构造函数添加了一个接受T
的std::forward
,因此它不会创建其参数的两个副本。 实际上,我们接受T
值并使用std::forward
。 如果const T&
和T&&
的重载是引用T
则会失败,因为这样T&&
也会折叠为左值引用,并且重载变得不明确。
#include <iostream>
#include <utility>
template <typename T>
class Wrapper
{
public:
Wrapper(T t) : m_t {std::forward<T>(t)}
{
}
Wrapper(Wrapper&& w) : m_t {std::forward<T>(w.m_t)}
{
}
Wrapper(const Wrapper&) = delete;
Wrapper&
operator=(Wrapper&& w)
{
if (this != &w)
this->m_t = std::move(w.m_t);
return *this;
}
Wrapper&
operator=(const Wrapper&) = delete;
private:
T m_t;
};
现在,让我们将其作为具有检测类型的试驾,让我们看到发生了什么。
struct A
{
A ()
{
std::cerr << "A was default-constructed" << std::endl;
}
A (const A&)
{
std::cerr << "A was copy-constructed" << std::endl;
}
A (A&&)
{
std::cerr << "A was move-constructed" << std::endl;
}
A&
operator=(const A&)
{
std::cerr << "A was copy-assigned" << std::endl;
return *this;
}
A&
operator=(A&&)
{
std::cerr << "A was move-assigned" << std::endl;
return *this;
}
~A ()
{
std::cerr << "A was destroyed" << std::endl;
}
};
int main()
{
A a {};
Wrapper<A> val1 {a};
Wrapper<A> val2 {std::move(val1)};
val1 = std::move(val2);
Wrapper<A&> ref1 {a};
Wrapper<A&> ref2 {std::move(ref1)};
ref1 = std::move(ref2);
}
使用 GCC 4.9.1 编译(整个练习实际上毫无意义,因为它支持已经开箱即用的各种移动)并且所有优化都已关闭,输出如下(添加了注释)。
A was default-constructed ; automatic variable a in main
A was copy-constructed ; copied as Wrapper<A>'s constructor argument
A was move-constructed ; forwarded in Wrapper<A>'s initializer
A was destroyed ; the moved-away from copy as the constructor returns
A was move-constructed ; forwarded in Wrapper<A>'s move-constructor
A was move-assigned ; move-assignment operator in Wrapper<A>
A was move-assigned ; not sure about this one... (apparently caused by the Wrapper<A&> move-assignment)
A was destroyed ; the sub-object of val2
A was destroyed ; the sub-object of val1
A was destroyed ; the automatic variable a in main
相关文章:
- 自定义先决条件对移动分配运算符有效吗
- C++ - 没有自定义交换功能的移动分配运算符?
- emplace_back和push_back给出错误'double free or corruption (fasttop)'尽管定义了复制和移动构造函数
- 当存在用户定义的移动分配运算符时,已删除模板移动分配运算符
- 移动 std::bitset<N> 是否超过 N 个位置未定义的行为?
- 在从仅移动类型派生的类中定义析构函数在使用 std::vector emplace_back或push_back创建时会
- 默认移动成员定义为已删除,而未定义特殊成员?
- 为什么定义移动构造函数会删除移动赋值运算符
- 自定义类中的移动与复制性能
- 为什么在定义析构函数时隐式删除移动构造函数
- 容器中需要显式移动构造函数,没有编译器定义的移动构造函数
- 编译器定义的移动构造函数与析构函数
- 为什么定义了移动构造函数并隐式删除了赋值运算符?
- 为什么将 operator() 的定义从 .h 移动到.cpp文件会导致数据竞争?
- MFC GUI 自定义控件:如何绘制光标更新以响应鼠标移动?
- Pimpl with unique_ptr : 为什么我必须将接口构造函数的定义移动到".cpp"?
- 如果将功能的非常简单的定义移动到.cpp,则编译时间的减少是多少
- 左值引用类型成员的用户定义移动构造函数
- 如何正确定义移动构造函数
- 如何定义移动构造函数