gpt4 book ai didi

c++ - 左值引用类型成员的用户定义 move 构造函数

转载 作者:搜寻专家 更新时间:2023-10-31 01:02:04 25 4
gpt4 key购买 nike

我在编译器上玩 move 语义,它有右值引用但不支持默认 move 构造函数。我想生成类似下面的包装器类的东西,即使模板参数是左值引用,它也能工作。但是,这种直接的方法无法编译,因为它试图从一个 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");
}

显而易见的解决方案是使用两个 move 构造函数,一个用于左值引用,一个用于所有其他类型。例如这样的事情:

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> 在 move 构造函数中。我还添加了一个 move 赋值运算符,但这是我不明白的地方:除非在其他地方,它需要 std::move而不是 std::forward !最后,我还添加了一个 std::forward到接受 T 的构造函数所以它不会为其参数创建两个拷贝。实际上需要我们接受 T按值(value)和用途 std::forward这里。为 const T& 重载和 T&&如果 T 会失败是一个引用,因为那时 T&&也会collapse到左值引用,重载变得不明确。

#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 编译(整个练习实际上毫无意义,因为它支持开箱即用的各种 move 。)并关闭所有优化,输出如下(添加注释)。

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++ - 左值引用类型成员的用户定义 move 构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27851258/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com