提问人:Andrew Voelkel 提问时间:3/15/2022 最后编辑:cigienAndrew Voelkel 更新时间:3/24/2022 访问量:209
复制具有继承功能的模板中的构造函数和赋值运算符
copy constructors and assignment operators in templates with inheritance
问:
template<typename T = uint8_t> class ArrayRef {
using uint = unsigned int;
protected:
ArrayRef() {}
ArrayRef(const ArrayRef&) {}
ArrayRef& operator=(const ArrayRef& other) { return *this; }
};
class ByteArray : ArrayRef<uint8_t> {
ByteArray(const ArrayRef&);
ByteArray& operator=(const ArrayRef&);
public:
ByteArray() {}
};
class Base {
using uint = unsigned int;
protected:
Base() {}
Base(const Base&) {}
Base& operator=(const Base& other) { return *this; }
};
class Derived : Base {
Derived(const Derived&);
Derived& operator=(const Derived& other) { return *this; }
public:
Derived() {}
};
int main() {
ByteArray ba;
ByteArray ba2 = ba; // no error, why?
ba = ba2; // no error why?
Derived d;
Derived d2 = d; // error (expected) - Calling a private constructor
d = d2; // error (expected) - Calling private assignment operator
}
关于上面代码的两个问题。
- 您能解释一下为什么模板化代码的行为与非模板化代码不同吗?(参见 main() 中的注释)。
- 我将如何为这样的模板化代码正确地创建私有副本构造函数和赋值运算符,以防止对象复制?
答:
2赞
Ted Lyngmo
3/15/2022
#1
- 您能解释一下为什么模板化代码的行为与非模板化代码不同吗?(参见 main() 中的注释)。
区别与模板无关。
区别在于,复制构造函数和复制赋值运算符在 中隐式定义(如 )。在你做了它们.如果将模板排除在问题之外,可能会更容易看到两个版本之间的差异:public
ByteArray
Derived
private
class ArrayRef {
protected:
ArrayRef() {}
ArrayRef(const ArrayRef&) {}
ArrayRef& operator=(const ArrayRef& other) { return *this; }
};
class ByteArray : ArrayRef {
ByteArray(const ArrayRef&); // your converting ctor
ByteArray& operator=(const ArrayRef&); // your converting assignment op
public:
ByteArray() {}
// copy contructor - implicitly defined:
// ByteArray(const ByteArray&) = default;
// copy assignment operator - implicitly defined:
// ByteArray& operator=(const ByteArray&) = default;
};
如果您现在尝试复制一个,它将像基于类模板实例时一样正常工作。将上面的内容与实际制作的复制构造函数和复制赋值运算符进行比较。ByteArray
ByteArray
Derived
private
- 我将如何为这样的模板化代码正确地创建私有副本构造函数和赋值运算符,以防止对象复制?
为了防止复制,您可以:delete
class ByteArray : private ArrayRef<uint8_t> {
public:
ByteArray(const ByteArray&) = delete;
ByteArray& operator=(const ByteArray&) = delete;
ByteArray() {}
};
或者让它们允许 S 和 S 制作副本:private
ByteArray
friend
class ByteArray : private ArrayRef<uint8_t> {
private:
ByteArray(const ByteArray&) { ... }; // or `= default`
ByteArray& operator=(const ByteArray&) { ...; return *this; } // or `= default`
public:
ByteArray() {}
};
请注意,将 作为输入的(转换)构造函数和(转换)赋值运算符不会阻止隐式定义的复制构造函数和复制赋值运算符将 作为输入,该运算符来自 中创建的输入。private
const ArrayRef<uint8_t>&
const ByteArray&
ByteArray
在您的类中,您实际上已经创建了复制构造函数和复制赋值运算符,这就是为什么您在尝试使用它们时会遇到预期的编译错误。Derived
private
下面是一个完整示例,其中包括 (转换) 构造函数和赋值运算符,以及 (用户定义的) 复制构造函数和复制赋值运算符。
#include <cstdint>
#include <iostream>
template<typename T = uint8_t> class ArrayRef {
protected:
ArrayRef() {}
ArrayRef(const ArrayRef&) {}
ArrayRef& operator=(const ArrayRef& other) { return *this; }
};
class ByteArray : ArrayRef<uint8_t> {
ByteArray(const ArrayRef&) { std::cout << "your converting ctor\n"; }
ByteArray& operator=(const ArrayRef&) {
std::cout << "your converting assignment op\n"; return *this;
}
public:
ByteArray(const ByteArray&) { std::cout << "copy ctor\n"; }
ByteArray& operator=(const ByteArray&) {
std::cout << "copy assignment op\n"; return *this;
}
ByteArray() {}
};
int main() {
ByteArray a;
ByteArray b = a;
a = b;
}
您可以在输出中看到,没有使用任何方法:private
copy ctor
copy assignment op
评论
2赞
Quimby
3/15/2022
我也许会澄清一下,这是复制 ctor,不是。因此,正如您所说,公共的生成是为了。Derived(const Derived&);
ByteArray(const ArrayRef&);
ByteArray
0赞
Ted Lyngmo
3/15/2022
@Quimby 好主意。注释已添加。
0赞
Andrew Voelkel
3/18/2022
这个答案对我来说没有意义。模板版本和非模板版本之间的代码看起来相同。事实上,我复制了模板化代码并将其转换为非模板化代码,以确保它们在其他方面是相同的。你能详细说明一下吗?
0赞
Ted Lyngmo
3/18/2022
@AndrewVoelkel我稍微更新了一下答案。我希望现在更有意义。
0赞
Andrew Voelkel
3/24/2022
我需要认真思考,因为我心中的问题仍然是为什么模板化版本的行为与非模板化版本不同,而答案并没有直接解决这个问题。可能是我犯了一些愚蠢的错误来回答这个问题,但我还没有时间弄清楚是否是这种情况。我会开始的,但我有几天的假期,所以可能需要几天时间才能找到必要的思考时间。
上一个:如何定义管道操作员?
下一个:使用赋值运算符而不是隐式构造函数
评论
ByteArray(const ArrayRef&);
ByteArray(const ByteArray&);