Idiomatic way to prevent slicing?

六月ゝ 毕业季﹏ 提交于 2019-12-12 07:31:55

问题


Sometimes it can be an annoyance that c++ defaults to allow slicing. For example

struct foo { int a; };
struct bar : foo { int b; };

int main() {
    bar x{1,2};
    foo y = x; // <- I dont want this to compile!
}

This compiles and runs as expected! Though, what if I dont want to enable slicing?

What is the idiomatic way to write foo such that one cannot slice instances of any derived class?


回答1:


I'm not sure if there is a named idiom for it but you can add a deleted function to the overload set that is a better match then the base classes slicing operations. If you change foo to

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
};

then you can only ever copy construct or copy assign a foo to foo. Any other type will pick the function template and you'll get an error about using a deleted function. This does mean that your class, and the classes that use it can no longer be an aggregate though. Since the members that are added are templates, they are not considered copy constructors or copy assignment operators so you'll get the default copy and move constructors and assignment operators.




回答2:


Since 2011, the idiomatic way has been to use 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
}

If you wish to actively prevent slicing, there are a number of ways:

Usually the most preferable way, unless you specifically need inheritance (you often don't) is to use encapsulation:

#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

}

Another more specialised way might be to alter the permissions around copy operators:

#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
}



回答3:


You can prevent the base from being copied outside of member functions of derived classes and the base itself by declaring the copy constructor protected:

struct foo {
    // ...
protected:
    foo(foo&) = default;
};


来源:https://stackoverflow.com/questions/55600025/idiomatic-way-to-prevent-slicing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!