有时,C ++默认允许切片可能会带来麻烦。例如
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
foo y = x; // <- I dont want this to compile!
}
这个compiles and runs as expected!但是,如果我不想启用切片怎么办?
写foo
使得不能对任何派生类的实例进行切片的惯用方式是什么?
答案 0 :(得分:20)
我不确定是否有命名的习惯用法,但是您可以向重载集中添加一个删除的函数,该函数比基类切片操作更匹配。如果您将foo
更改为
struct foo
{
int a;
foo() = default; // you have to add this because of the template constructor
template<typename T>
foo(const T&) = delete; // error trying to copy anything but a foo
template<typename T>
foo& operator=(const T&) = delete; // error assigning anything else but a foo
};
然后,您只能复制构造或将foo
分配给foo
。任何其他类型都会选择功能模板,并且会出现有关使用已删除功能的错误信息。这确实意味着您的类以及使用它的类不再可以聚合。由于添加的成员是模板,因此不将它们视为复制构造函数或复制赋值运算符,因此您将获得默认的复制并移动构造函数和赋值运算符。
答案 1 :(得分:5)
自2011年以来,惯用的方法是使用auto
:
#include <iostream>
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
auto y = x; // <- y is a bar
}
如果您希望积极防止切片,可以采用多种方法:
通常,最可取的方法是使用封装,除非您特别需要继承(您通常不需要)()
#include <iostream>
struct foo { int a; };
struct bar
{
bar(int a, int b)
: foo_(a)
, b(b)
{}
int b;
int get_a() const { return foo_.a; }
private:
foo foo_;
};
int main() {
bar x{1,2};
// foo y = x; // <- does not compile
}
另一种更专业的方法可能是更改复制操作员周围的权限:
#include <iostream>
struct foo {
int a;
protected:
foo(foo const&) = default;
foo(foo&&) = default;
foo& operator=(foo const&) = default;
foo& operator=(foo&&) = default;
};
struct bar : foo
{
bar(int a, int b)
: foo{a}, b{b}
{}
int b;
};
int main() {
auto x = bar (1,2);
// foo y = x; // <- does not compile
}
答案 2 :(得分:3)
通过声明复制构造函数受保护,可以防止将基类复制到派生类的成员函数和基类本身之外:
struct foo {
// ...
protected:
foo(foo&) = default;
};