防止切片的惯用方法?
Posted
技术标签:
【中文标题】防止切片的惯用方法?【英文标题】:Idiomatic way to prevent slicing? 【发布时间】:2019-04-09 19:32:23 【问题描述】:有时,c++ 默认允许切片可能会令人烦恼。例如
struct foo int a; ;
struct bar : foo int b; ;
int main()
bar x1,2;
foo y = x; // <- I dont want this to compile!
这个compiles and runs as expected!不过,如果我不想启用切片怎么办?
写foo
的惯用方法是什么,这样就不能对任何派生类的实例进行切片?
【问题讨论】:
最简单的方法是不使用继承。你有foo
是bar
的成员变量。
@KevinZ ***.com/a/55600208/4117728
【参考方案1】:
我不确定它是否有一个命名习惯用法,但您可以向重载集添加一个已删除的函数,该函数比基类切片操作更匹配。如果您将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
。任何其他类型都会选择函数模板,并且您会收到有关使用已删除函数的错误。这确实意味着您的类和使用它的类不再是一个聚合。由于添加的成员是模板,因此它们不被视为复制构造函数或复制赋值运算符,因此您将获得默认的复制和移动构造函数和赋值运算符。
【讨论】:
请注意,这不会阻止像这样的显式切片:foo y = static_cast<foo&>(x);
。也就是说,也许这对 OP 来说不是问题。
如果我理解正确,这是防止函数参数隐式转换的好方法
@user463035818 Yep。自从我问了那个问题后,我就一直在使用它。
我将其视为反向 SFINAE。您制作要编译的重载,然后添加一个已删除的模板来阻止其他所有内容。
其实我有点犹豫接受这个答案。该技术很棒,但实际上它为专门处理各种不需要的任务打开了大门,尽管如果我必须在 Java 语言“不惜一切代价防止一切可能的愚蠢行为”与 Python 语言“我们都是成年人”之间做出选择,那么我知道该选什么;)【参考方案2】:
自 2011 年以来,惯用方式一直是使用 auto
:
#include <iostream>
struct foo int a; ;
struct bar : foo int b; ;
int main()
bar x1,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 x1,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)
: fooa, bb
int b;
;
int main()
auto x = bar (1,2);
// foo y = x; // <- does not compile
【讨论】:
【参考方案3】:您可以通过声明复制构造函数 protected 来防止基类被复制到派生类的成员函数和基类本身之外:
struct foo
// ...
protected:
foo(foo&) = default;
;
【讨论】:
但是我不能再复制foo
s :( 如果可能的话,我想防止只将 bar 复制到 foo 以上是关于防止切片的惯用方法?的主要内容,如果未能解决你的问题,请参考以下文章