#c #casting #overloading
#c #Кастинг #перегрузка
Вопрос:
Приведения типов в стиле C (все 4) выглядят точно так же, как некоторые шаблоны функций. например
template<typename TO, typename FROM>
TO dynamic_cast (FROM p);
будет использоваться как,
dynamic_cast<Derived*>(p); // p is Base*
Почему не разрешается перегружать их стандартом языка для пользовательского использования? (например, мы можем перегрузить ключевые слова like new/delete
или other operators
)
Комментарии:
1. Интересно, что некоторые библиотеки (например, Boost) используют это сходство для определения своих собственных операторов «приведения», например
static_pointer_cast<>
.2. @ereOne, ya Я слышал о некоторых приведениях, подобных этому, но это ключевое слово по какой-то причине не разрешено!!
3. Поместите в свой проект правило, согласно которому все приведения будут выполняться с помощью некоторых шаблонов пересылки: «x_dynamic_cast», «x_static_cast» и т.д. Затем найдите любое использование реального dynamic_cast и замените его. Это одна из замечательных особенностей «многословных» операторов приведения, поскольку их очень легко искать.
4. Поскольку есть некоторые ответы, которые в значительной степени охватывают часть «почему», я просто хотел сказать, что, возможно, это запрещено, но вы можете заменить dynamic_cast. Вы можете определить свой шаблон «my_cast», а затем использовать препроцессор для замены dynamic_cast или переинтерпретации dynamic_cast, скажем, в отладочных сборках (т. Е. таким же образом вы можете использовать препроцессор для «переименования» new в DEBUG_NEW). Мы сделали это, добавив некоторые проверки во время компиляции в отладочные сборки, связанные с приведением. У нас было преимущество в том, что у нас был файл заголовка, который был включен поверх всех, чтобы мы могли ‘#define dynamic_cast chk:my_cast’ в одном месте
5. Преимущество, которое я вижу в подходе препроцессора (по сравнению с заменой с помощью search и заменой dynamic_cast на x_my_dynamic_cast, заключается в том, что это не заставляет всех в вашей команде писать x_my_dynamic_cast, а также гарантирует, что ситуации, когда кто-то забывает (или еще не знает — т. Е. является новым в команде), что x_my_dynamic_cast вместо dynamic_cast следует использовать, автоматически предусмотрены. И каждый может писать код, как обычно…
Ответ №1:
Почему не разрешается перегружать их стандартом языка для пользовательского использования?
Я полагаю, это потому, что комитет по стандартизации, представляя их, думал, что семантика всех четырех из этих приведений четко определена и применима ко всем типам, которыми они должны быть. И в основном, это правда.
Единственный встречный пример, о котором я знаю, — это невозможность dynamic_cast
перехода между экземплярами интеллектуального указателя:
shared_ptr<Derived> pd = dynamic_cast<shared_ptr<Derived> >(pb);
Я полагаю, что возможность сделать это имела бы некоторые преимущества.
Я не знаю, обсуждалось ли это добровольцами, которые выполняли всю работу в комитете по стандартам (и я слишком ленив, чтобы гуглить), но если это обсуждалось (а я бы так подумал), это было отклонено либо потому, что кто-то подумал, что недостатки перевешивают преимущества, либо потому, что никто не нашел времени, чтобы сделать достойное предложение и довести его до конца.1
1 Не смейтесь. На самом деле есть много вещей, которые, по мнению большинства, было бы неплохо иметь, но которые не могут быть реализованы только потому, что никто не потрудился написать достойное предложение и потратить время, необходимое для обсуждения и многократного улучшения его, пока за него не можно будет проголосовать.
Комментарии:
1. 1, я отнесся к этому серьезно. 🙂 Я чувствую, что любому приведению должно быть разрешено быть перегруженным. Даже несмотря на то, что доступны их стандартные достойные реализации. например, я хочу выполнить отладку, когда кто-то попытается
reinterpret_cast
.2. @iammilind: Скажем, в перегрузке нет смысла
static_cast
. Что бы вы хотели сделать в перегруженной версии? То же самое касаетсяreinterpret_cast
. Оно предназначено для выполнения небезопасных, зависящих от платформы приведений. Что бы вы хотели добавить к тому, чего он еще не делает? И не заставляйте меня начинать сconst_cast
. Все, что я мог бы себе представить, что делаю с перегрузкой этого, должно быть выполнимо с надлежащим кодом, корректным в отношении констант.)3. Вы уже можете повлиять
static_cast
, предоставив конструктор преобразования или оператор преобразования для его использования.4. @BoPersson: Да, я забыл упомянуть об этом. Спасибо.
5. Я также не вижу смысла в перегрузке static_cast, reinterpret_cast и const_cast … но перегрузка dynamic_cast — это то, чего я также хотел бы иметь.
Ответ №2:
Я думаю, причина та же, что и для ключевого слова you can’t overload language.
На самом деле, вы должны рассматривать их как ключевое слово языка, а не как шаблонную функцию, даже если они выглядят одинаково. ОТО, я не мог представить, каких катастроф можно было бы натворить, изменив значение этого конкретного аспекта C .
Редактировать
Я был почти уверен, что у кого-нибудь возник бы вопрос: «тогда почему вы можете перегружать new
/ delete
?». Я думаю, что настройка выделения / освобождения памяти — это то, что вам нужно в определенных сценариях, и преимущества, позволяющие вам перегружать их, перевешивают риски. Я не вижу никакого преимущества в подрыве системы типов C , поэтому я не могу придумать сценарий, в котором это было бы полезно. А ты?
Комментарии:
1.
new/delete
это также ключевое слово языка, и ppl может привести к сбоям в них; то же самое применимо для перегрузкиoperator
. Тогда почему не приведения типов, которые уже точно выглядят как функции.2.
shared_ptr<Derived> pd = dynamic_cast<shared_ptr<Derived> >(pb)
3. да,
dynamic_cast
может быть оптимизировано, если разработчик знает, что его класс наследуется только одним другим классом (с виртуальной функцией). Есть много таких полезных функций, которые я могу использовать для других приведений4. @iammilind если вам нужно оптимизировать свой код, я бы не стал зацикливаться на приведении типов. Мне действительно трудно думать об этом как о узком месте…
5. @Simone, иногда ваш класс может использоваться другими. Вы не можете попросить их не использовать
dynamic_cast
; скорее вы можете просто перегрузить его. Кроме того, я только что привел пример .. таких полезных функций может быть много.
Ответ №3:
Преобразование указателей с помощью dynamic_cast
, reinterpret_cast
и static_cast
имеют четко определенные значения, и, вероятно, лучше не допускать перегрузки.
Было бы затруднительно разрешить пользователям изменять значение const_cast
.
Остается только приведение типов объектов.
struct A
{
A() {};
template <typename FROM>
A(FROMamp;) {
std::cout << "Casting to A \o/" << std::endl;
}
template <typename TO>
operator TO() {
std::cout << "Casting from A \o/" << std::endl;
return TO();
}
};
затем
int i; A a;
A toA = static_cast<A>(i); // Casting to A o/
int fromA = static_cast<int>(a); // Casting from A o/
Надеюсь, у вас есть варианты использования получше, чем у меня 🙂
Ответ №4:
Вы не можете перегружать эти операторы. Возможно, это потому, что вы не можете изменить значение такой фундаментальной вещи, как говорят другие ответы. (Например, изменение значения для
целых чисел или *
оформления для генерации указателя на тип).
Сказав это, ничто не мешает вам определить ваши собственные обобщения приведенных функций, которые принимают аргумент и возвращают вещи, очень связанные с ним. На самом деле, я бы высказал противоположную точку зрения, вы никогда не должны использовать операции приведения языка ( static/dynamic/reinterpret_cast
), если вы не делаете довольно низкоуровневые вещи.
Что вы, вероятно, хотели бы сделать, так это определить свою собственную функцию приведения, которая большую часть времени ведет себя подобно функциям, предоставляемым языком, но время от времени они делают что-то более специфичное для ваших конкретных целей. Вы должны хорошенько подумать, что на самом деле делает эта функция, и правильно назвать ее. Какую стоимость времени выполнения вы можете себе позволить, какое поведение при «сбое» (выбросить выполнение?, вернуть нулевое значение?) и т.д.
Стандарт и многие библиотеки полны таких функций. Иногда они добавляют измененное поведение поверх приведения языка, в других случаях они делают больше. Некоторые примеры:
https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast
std::static_pointer_cast
,std::dynamic_pointer_cast
,std::const_pointer_cast
,-
std::reinterpret_pointer_cast
-
std::any_cast
std::chrono::time_point_cast
(смотрите также https://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/pointer_cast.html )
https://www.boost.org/doc/libs/1_42_0/libs/conversion/lexical_cast.htm
boost::lexical_cast
https://www.boost.org/doc/libs/1_63_0/libs/conversion/cast.htm
polymorphic_cast
,polymorphic_downcast
,polymorphic_pointer_cast
polymorphic_pointer_downcast
boost::numeric_cast
boost::units::quanity_cast
.
Иногда они вообще не вызываются cast
🙂
https://en.cppreference.com/w/cpp/utility/variant/get_if
std::get_if
Другой пример, для кода шаблона я написал эту функцию приведения, которая вызывается только в том случае, если преобразование может быть выполнено неявно:
template<class To, class From, std::enable_if_t<std::is_convertible<From, To>{}, int> =0>
To implicit_cast(Fromamp;amp; f){
return static_cast<To>(f);
}