Подтвердить что ты не робот

Является ли предметная нарезка когда-либо полезной?

Обрезание объектов происходит, когда мы назначаем или копируем объект производного класса в объект его базового класса, теряя производную часть его в процессе.

Здесь более подробно объяснено: Что такое проблема нарезания на С++?.

(Сам я не рассматриваю его как проблему, а скорее естественное следствие семантики значения языка, но это не вопрос этого вопроса.)

Что я задаюсь вопросом: существуют ли когда-нибудь ситуации, когда вы будете использовать это намеренно? Ситуация, где это "правильный инструмент для работы"?

4b9b3361

Ответ 1

Конечно, это может быть полезно, если вы хотите удалить производную часть класса, возможно, чтобы отбросить зависимости.

Например, у нас есть установка объектной системы, где каждая база принадлежит производному типу, и каждый производный тип имеет различные зависимости, возможно, выполненные посредством инъекции зависимостей. Клон базы может захотеть быть создан, хотя может потребоваться назначение совершенно нового набора зависимостей для фактического производного типа этой базы.

Это можно сравнить с движком игры, где есть много типов коллайдеров. Каждый коллайдер происходит от базового интерфейса-объекта по-разному. Мы хотим клонировать коллайдер, чтобы получить его положение и масштаб (от базы), но хотим поместить совершенно другую производную реализацию поверх этой базы. "Обрезка объектов" может быть простым способом достижения этого.

На самом деле компонент или организация совокупного объекта будет иметь гораздо больше смысла, чем конкретное разделение объектов, но в основном это одна и та же идея.

Ответ 2

Некоторые реализации STL на самом деле используют обрезку объектов для реализации алгоритмов: Например, используя iterator_tags вы можете легко сделать std::advance использовать наиболее эффективный алгоритм:

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

Используя собственную иерархию классов, вы можете устранить неоднозначные перегрузки функций. Например. для преобразования объекта в std::string вы можете использовать функцию-член объектов to_string(), если она существует или используется иным образом 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);
}