gpt4 book ai didi

使用模板复制和移动构造函数的 C++ 类型删除

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:35:45 26 4
gpt4 key购买 nike

我最近开始学习类型删除。事实证明,这项技术可以大大简化我的生活。因此我试图实现这种模式。但是,我遇到了类型删除类的复制和移动构造函数的一些问题。现在,让我们先看一下代码,它非常简单

#include<iostream>
class A //first class
{
private:
double _value;
public:
//default constructor
A():_value(0) {}
//constructor
A(double v):_value(v) {}
//copy constructor
A(const A &o):_value(o._value) {}
//move constructor
A(A &&o):_value(o._value) { o._value = 0; }

double value() const { return _value; }
};

class B //second class
{
private:
int _value;
public:
//default constructor
B():_value(0) {}
//constructor
B(int v):_value(v) {}
//copy constructor
B(const B &o):_value(o._value) {}
//move constructor
B(B &&o):_value(o._value) { o._value = 0; }

//some public member
int value() const { return _value; }
};

class Erasure //the type erasure
{
private:
class Interface //interface of the holder
{
public:
virtual double value() const = 0;
};

//holder template - implementing the interface
template<typename T> class Holder:public Interface
{
public:
T _object;
public:
//construct by copying o
Holder(const T &o):_object(o) {}
//construct by moving o
Holder(T &&o):_object(std::move(o)) {}
//copy constructor
Holder(const Holder<T> &o):_object(o._object) {}
//move constructor
Holder(Holder<T> &&o):_object(std::move(o._object)) {}

//implements the virtual member function
virtual double value() const
{
return double(_object.value());
}
};

Interface *_ptr; //pointer to holder
public:
//construction by copying o
template<typename T> Erasure(const T &o):
_ptr(new Holder<T>(o))
{}

//construction by moving o
template<typename T> Erasure(T &&o):
_ptr(new Holder<T>(std::move(o)))
{}

//delegate
double value() const { return _ptr->value(); }
};

int main(int argc,char **argv)
{
A a(100.2344);
B b(-100);

Erasure g1(std::move(a));
Erasure g2(b);

return 0;
}

作为编译器,我在 Debian 测试系统上使用 gcc 4.7。假设代码存储在名为 terasure.cpp 的文件中,构建会导致以下错误消息

$> g++ -std=c++0x -o terasure terasure.cpp
terasure.cpp: In instantiation of ‘class Erasure::Holder<B&>’:
terasure.cpp:78:45: required from ‘Erasure::Erasure(T&&) [with T = B&]’
terasure.cpp:92:17: required from here
terasure.cpp:56:17: error: ‘Erasure::Holder<T>::Holder(T&&) [with T = B&]’ cannot be overloaded
terasure.cpp:54:17: error: with ‘Erasure::Holder<T>::Holder(const T&) [with T = B&]’
terasure.cpp: In instantiation of ‘Erasure::Erasure(T&&) [with T = B&]’:
terasure.cpp:92:17: required from here
terasure.cpp:78:45: error: no matching function for call to ‘Erasure::Holder<B&>::Holder(std::remove_reference<B&>::type)’
terasure.cpp:78:45: note: candidates are:
terasure.cpp:60:17: note: Erasure::Holder<T>::Holder(Erasure::Holder<T>&&) [with T = B&]
terasure.cpp:60:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘Erasure::Holder<B&>&&’
terasure.cpp:58:17: note: Erasure::Holder<T>::Holder(const Erasure::Holder<T>&) [with T = B&]
terasure.cpp:58:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘const Erasure::Holder<B&>&’
terasure.cpp:54:17: note: Erasure::Holder<T>::Holder(const T&) [with T = B&]
terasure.cpp:54:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘B&’

似乎对于 Erasure g2(b); 编译器仍然尝试使用移动构造函数。这是编译器的预期行为吗?我是否误解了类型删除模式的一般含义?有人知道如何解决这个问题吗?

最佳答案

从编译器错误可以明显看出,编译器正在尝试实例化您的 Holder T = B& 的类别.这意味着该类将存储引用类型的成员,这会给您带来一些复制等问题。

问题在于T&& (对于推导的模板参数)是一个通用引用,这意味着它将绑定(bind)到所有内容。对于 B 的 r 值它会推导出T成为B并绑定(bind)为右值引用,对于左值,它将推导出 T成为B&并使用引用折叠来解释 B& &&作为B& (对于 const B 左值,它会将 T 推断为 const B& 并进行折叠)。在你的例子中 b是一个可修改的左值,使构造函数采用 T&& (推断为 B& )比 const T& 更好的匹配(推断为 const B& )一个。这也意味着 Erasure构造函数 const T&并不是真正必要的(与 Holder 不同,因为 T 没有被该构造函数推导)。

这个问题的解决方案是在创建持有者类时从类型中去除引用(可能还有常量,除非你想要一个 const 成员)。您还应该使用 std::forward<T>而不是 std::move ,因为如前所述,构造函数也绑定(bind)到左值,从这些值移动可能不是一个好主意。

    template<typename T> Erasure(T&& o):
_ptr(new Holder<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(o))
{}

您的 Erasure 中还有另一个错误类,它不会被编译器捕获:你存储你的 Holder在指向堆分配内存的原始指针中,但既没有自定义析构函数来删除它,也没有自定义处理复制/移动/分配(三/五规则)。解决该问题的一种选择是实现这些操作(或使用 =delete 禁止不重要的操作)。然而这有点乏味,所以我个人的建议是不要手动管理内存,而是使用 std::unique_ptr。用于内存管理(不会给你复制能力,但如果你想要,你首先需要扩展你的 Holder 类以进行克隆)。

其他需要考虑的要点:为什么要为 Erasure::Holder<T> 实现自定义复制/移动构造函数? , AB ?默认的应该很好,不会禁用移动赋值运算符的生成。

还有一点是Erasure(T &&o)是有问题的,因为它将与复制/移动构造函数竞争( T&& 可以绑定(bind)到 Èrasure& 这是比 const Erasure&Erasure&& 更好的匹配)。为避免这种情况,您可以使用 enable_if检查 Erasure 的类型,给你类似这样的东西:

    template<typename T, typename Dummy = typename std::enable_if<!std::is_same<Erasure, std::remove_reference<T>>::value>::type>
Erasure(T&& o):
_ptr(new Holder<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(o))
{}

关于使用模板复制和移动构造函数的 C++ 类型删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13828468/

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