我有这段代码:
template <typename T>
class EasyPtr {
T* data;
public:
EasyPtr(T* data): data(data){}
EasyPtr(const EasyPtr<T>&)=delete;
EasyPtr(EasyPtr<T>&&) = delete;
~EasyPtr(){
delete data;
}
operator T*(){
return data;
}
EasyPtr & operator = (T* data) {
this -> data = data;
}
T& operator *() {
return *data;
}
T& operator [](size_t pos) {
return data[pos];
}
};
int main(){
EasyPtr<int> p = new int[10];
return 0;
}
令人惊讶的是它给出了错误:
In file included from easy_ptr_test.cpp:1:0:
./easy_ptr.hpp:23:20: error: declaration of ‘operator[]’ as non-function
./easy_ptr.hpp:23:18: error: expected ‘;’ at end of member declaration
./easy_ptr.hpp:23:27: error: expected ‘)’ before ‘pos’
easy_ptr_test.cpp: In function ‘int main()’:
easy_ptr_test.cpp:4:32: error: use of deleted function ‘EasyPtr<T>::EasyPtr(EasyPtr<T>&&) [with T = int]’
In file included from easy_ptr_test.cpp:1:0:
./easy_ptr.hpp:10:5: error: declared here
这显然是一个功能声明......不明白为什么。也许某处有一个愚蠢的错误。
答案 0 :(得分:2)
类型size_t
在cstddef
标头中定义,因此在使用之前您应该#include
。这应该摆脱你operator[]
声明的错误。
main
中的错误是由于复制初始化的工作方式而产生的。当你这样做
EasyPtr<int> p = x;
您正在执行复制初始化。如果x
的类型为EasyPtr<int>
,或者它的cv限定版本或其派生类之一,则此初始化的效果只是调用{{1的复制或移动构造函数(不是复制赋值或移动赋值运算符!)如果EasyPtr<int>
属于不同类型,则x
首先用于构造临时 x
对象,然后然后调用EasyPtr<int>
的复制或移动构造函数,以将此临时对象复制或移动到EasyPtr<int>
。您收到错误,因为编译器想要调用移动构造函数但您已将其删除。 (同样,复制赋值和移动赋值运算符不被调用。)
解决方案:
1)明确定义移动构造函数
p
2)显式默认移动构造函数(您可能不希望为此特定类执行此操作,因为它将导致双重释放,但为了完整性而包含此解决方案)
EasyPtr(EasyPtr<T>&& other): data(other.data) {
other.data = nullptr;
}
3)使用直接初始化而不是复制初始化
EasyPtr(EasyPtr<T>&&) = default;
最后一个选项可能是您想要的选项,因为您似乎不希望复制或移动对象。在这种情况下,复制初始化不起作用,因此您应该声明转换构造函数EasyPtr<int> p {new int[10]};
,因此错误消息将不那么混乱。
explicit
答案 1 :(得分:1)
EasyPtr<int> p = new int[10];
main中的那一行隐含地构建了右侧的临时EasyPtr<int>
。从概念上讲,它看起来像这样:
EasyPtr<int> p = EasyPtr<int>(new int[10]);
此临时文件可以移至p
。如果您的编译器实现了copy-elision,那么将该临时文件移动到p
甚至可以省略;但为了实现这一点,移动构造函数仍然必须是可访问的。因为移动构造函数声明为delete
'd,所以您收到错误。如果您将其更改为EasyPtr<int> p(new int[10])
,则错误应该消失,因为您不再从另一个p
实例构建EasyPtr
。