Почему не рекомендуется использовать рекурсивное наследование для реализации std:: tuple?

В этом вопросе Говард Хиннант сказал

Некоторые реализации std:: tuple используют рекурсивное наследование. Но хорошие - нет.; -)

Может кто-то пролить свет на это?

Ответ 1

Нерекурсивная реализация имеет лучшую производительность во время компиляции. Верьте или нет, в сильно используемом библиотечном средстве, таком как std::tuple, как это реализовано, может повлиять (к лучшему или худшему) время компиляции, которое видит клиент. Рекурсивные реализации, как правило, производят время компиляции, линейное по глубине рекурсии (или может быть еще хуже).

Это влияет не только на создание самого кортежа. std::get<I>(tuple), например, будет брать линейное количество времени компиляции для одной реализации и постоянное количество времени компиляции для другой реализации. Это влияние может быстро ухудшаться (или нет) при работе с кортежами кортежей. То есть рекурсивная реализация может привести к времени компиляции O (N ^ 2), в то время как нерекурсивная реализация все еще O (1).

Fwiw, реализация libС++ выдает объекты в порядке, указанном клиентом, но оптимизирует пространство для пустых компонентов, используя средство оптимизации пустого базового класса компилятора.

Ответ 2

Я не помню, как Андрей Александреску GoingNative 2012 говорил точно, но он говорил об этом, и одним из моментов, которые он упомянул, была макет памяти. Если у меня есть std::tuple<int, short, char, char>, он будет в памяти как char, short, int, и этот макет возьмет (в моей системе) 4 байта больше, чем если бы они были выложены как int, short, char. R. Martinho Fernandes напомнил мне, что лучше всего будет заказать их в памяти в порядке, который минимизирует заполнение, которое не является ни порядком, ни порядком обратного. (Наивное наследование делает обратный порядок).

Если я пишу std::tuple<int, char, short, char>, кортеж, который работает по наивному наследованию, поместил бы их в порядок char, short, int в памяти, используя 3 байта заполнения, когда оптимальный имеет нулевые байты заполнения. (Либо int, short, char, char, либо char, char, short, int).

Предполагая, что я прав, что это касается дополнения, тогда Р. Мартиньо Фернандес сказал "[мой аргумент] не исключает использования рекурсивного наследования для фактического реализация в оптимальном порядке.", поэтому я указываю, что наивное наследование плохое.

(порядок в памяти не означает, что get<0> даст другой объект, а Р. Мартиньо Фернандес правильно отмечает, что заказ должен быть невидимым для пользователя. Однако это были те моменты, которые мне напомнили из событие GoingNative.)

Видео находится в http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Variadic-Templates-are-Funadic, а слайды находятся на http://ecn.channel9.msdn.com/events/GoingNative12/GN12VariadicTemplatesAreFunadic.pdf.

Ответ 3

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