سؤال

Object slicing happens when we assign or copy an object of derived class to an object of its base class, losing the derived part of it in the process.

It has been explained in more depth here: What is the slicing problem in C++?.

(Myself, I don't see it as a problem, rather a natural consequence of language's value semantics, but that's not the point of this question.)

What I wonder is: are there ever situations where you'd use it purposedly? A situtation where it is the "right tool for the job"?

هل كانت مفيدة؟

المحلول

Sure, it can be useful when wanting to drop the derived portion of class, perhaps to drop dependencies.

For example say we have an object system setup where each base belongs to a derived type, and each derived type has various dependencies, perhaps fulfilled through dependency injection. A clone of a base might want to be created, though a completely new set of dependencies for the actual derived type of that base may want to be assigned.

This can be likened to an game engine where there are many types of colliders. Each collider derives from a base interface-like object in various ways. We want to clone a collider to retrieve it's position and scale (from base), but want to place an entirely different derived implementation on-top of this base. "Object slicing" could be a simple way to achieve this.

In reality a component, or aggregate object organization would make a lot more sense than object slicing specifically, but it's mostly the same idea.

نصائح أخرى

Some STL implementations actually use object slicing to implement algorithms: E.g., using iterator_tags you can easily make std::advance use the most efficient algorithm:

namespace std {

template <class I>
void advance_impl(I& it, int n, input_iterator_tag) {
    for (; n > 0; --n)
        ++it;
}

// Forward Iterators use the same implementation as Input Iterators...

// TODO:
// Add bidirectional_iterator_tag implementation...

template <class I>
void advance_impl(I& it, int n, random_access_iterator_tag) {
    it += n;
}

template <class I>
void advance(I& it, int n) {
    advance_impl(it, n, typename iterator_traits<I>::iterator_category());
}

} // std

Using your own little class hierarchy you can disambiguate otherwise ambiguous function overloads. E.g. to convert an object to a std::string you might want to use the objects member function to_string() if it exists or otherwise use operator<<.

struct R2 {};       // rank 2
struct R1 : R2 {};  // rank 1

// C++11.
// Use some type traits and enable_if in C++03.
template <class T>
auto ToString(R1, T const& t) -> decltype(t.to_string()) {
    return t.to_string();
}

template <class T>
std::string ToString(R2, T const& t) {
    std::ostringstream s;
    s << t;
    return s.str();
}

template <class T>
std::string ToString(T const& t) {
    return ToString(R1(), t);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top