Поскольку наличие одинаковых членов данных как в базовом, так и в производном классах, создает много путаницы и требует использования оператора разрешения области действия для разрешения конфликтов. Так почему это разрешено в C ++? Кто-нибудь может показать мне необходимость в этом?
Я не знаю точную мотивацию, но я считаю, что это простое продолжение нескольких подобных случаев, где это неизбежно. Рассмотрим, например, множественное наследование — многие базовые классы могут иметь одинаковые члены, и в принципе вы ничего не можете с этим поделать как создатель производного класса. Еще хуже для CRTP — вы не можете знать членов базового класса, так как он произвольный. Эти случаи кажутся менее запутанными, чем предмет вашего вопроса, и гораздо более проблематичны, поскольку их нельзя просто запретить, не нанося вред некоторым функциям. Так как проблема неоднозначности должна решаться в любом случае, кажется естественным, что этот конкретный случай решается с использованием тех же единых правил.
Затмение не всегда плохо. Один контрпример, где теневое копирование очень важно, — это когда мы работаем с вариационными шаблонами (особенно с кортежами)
Пример: рассмотрим следующую упрощенную реализацию кортежа. Это был первый пример, который я увидел, как использовать шаблоны с переменными числами.
template<typename... T> class tuple0;
template<> class tuple0<> {}; // end recursion
template<typename Head, typename... Tail>
class tuple0<Head, Tail...> : public tuple0<Tail...> {
public:
Head head;
};
Предположим, теперь мы хотим создать tuple0<int, double>
и получить доступ к обоим элементам. Вот тестовая программа, которая делает это
int main()
{
tuple0<int, double>* t1 = new tuple0<int, double>;
t1->head = 7; // set the integer value
std::cout << "integer: " << t1->head << std::endl;
tuple0<double>* t2 = static_cast< tuple0<double>* >(t1);
t2->head = std::cos(2); // set the double value
std::cout << "double: " << t2->head << std::endl;
return 0;
}
Здесь вы можете видеть, что без затенения было бы намного сложнее работать с вариадическими шаблонами. Кроме того, получить<> Метод в std :: tuple имеет аналогичную реализацию.
В разумном дизайне это никогда не должно быть проблемой. Если ты сознательно создавая элементы с тем же именем, что и у ваших баз, проблема заключается в вашем дизайне. если ты незнанию сделай это, ты не заметишь.
С другой стороны, если это было запрещено на уровне языка, они незнанию часть станет серьезной ошибкой. Рассмотрим использование фреймворка, от которого вы наследуете. Теперь рассмотрим, что есть общедоступный интерфейс, который хорошо документирован, но все, что является приватным, не документировано. Теперь вам нужно наследовать от типа (скажем, Window
) и у вас есть эта переменная с красивым значимым именем, которое имеет весь смысл в мире. Вы добавляете это к своему типу, запускаете компилятор только для того, чтобы узнать, что имя уже использовалось в базовом типе (или где-то в иерархии) …