Какие другие полезные приемы могут использоваться в С++
С++ поставляется с четырьмя встроенными приведениями.
-
static_cast
-
dynamic_cast
-
const_cast
-
reinterpret_cast
Не означает, что он нахмурился C (style*)cast
.
Дополнительно boost предоставляет lexical_cast, есть ли другие полезные приемы, которые вы используете или хотели бы существовать?
Ответы
Ответ 1
Мой любимый и самый любимый актерский состав implicit_cast
. Это удается только в том случае, если типы могут быть неявно преобразованы.
Полезно для преобразования из некоторого типа в void*
или из некоторого производного класса в базу (если вы хотите выбрать конкретный экземпляр перегруженной функции или конструктора) или безопасно добавить const-квалификацию и любой другой сценарий, в котором вы действительно просто нужны неявные преобразования, и даже static_cast
слишком мощный.
Также читайте Как С++ выбирает, какую перегрузку вызывать.
boost/implicit_cast.hpp
. Вы также можете добавить это в свою коллекцию кода, если хотите
template<typename T> struct identity { typedef T type; };
template<typename Dst> Dst implicit_cast(typename identity<Dst>::type t)
{ return t; }
Ответ 2
Также существует функциональный стиль, который выглядит как вызов функции или конструктора. Это разрешает вызов конструктора для классов и (в более общем смысле) для C-стиля для всех других типов.
Примеры:
int x = int(1.0); // equals `(int)1.0`
string s = string("x"); // equals call to constructor
Вызов конструктора также может быть достигнут с использованием явного приведения (кроме каста C-стиля, который также будет работать):
string s = static_cast<string>("x"); // is the same as above!
Ответ 3
Стоит помнить, что конструкторы также могут считаться действующими как отбрасывания и будут использоваться компилятором для выполнения подобных преобразований. Например:
class Person {
public:
Person( const std::string & name );
...
};
Конструктор Person выполняет преобразование из строки → Person:
Person p = Person( "fred" );
и будет использоваться компилятором, когда строка должна конвертироваться в человека:
void PrintPerson( const Person & p ) {
...
}
компилятор теперь может преобразовать строку в Person:
string name = "fred";
PrintPerson( name );
но обратите внимание, что он не может этого сделать:
PrintPerson( "fred" );
поскольку для этого потребуется, чтобы компилятор построил цепочку преобразований.
Изменить: Я опубликовал следующий вопрос по теме конверсий - см. неявные преобразования С++.
Ответ 4
Если вы используете shared_ptr, вы можете использовать броски указателей boost (boost:: static_pointer_cast,...). Они также могут использоваться для стандартных указателей.
Ответ 5
Одно действительно полезное повышение - оператор (функция действительно) - это
numeric_cast (число);
Это проверяет, что номер, который вы выполняете, находится в диапазоне для целевого типа.
например,
long long big_number = ....
int num = numeric_cast<int>(big_number); // throws an exception is big_number is too big
http://www.boost.org/doc/libs/1_39_0/libs/numeric/conversion/doc/html/boost_numericconversion/improved_numeric_cast__.html
Ответ 6
Существует также ужасный union_cast.
Это плохо, потому что, строго говоря, это UB, но если вы знаете, что делаете, может быть полезно преобразовать указатели на функции-члены в void*
и обратно, не все компиляторы позволяют сделать это с помощью reinterpret_cast.
Но все равно лучше избегать.
Ответ 7
ACE имеет truncate_cast. В основном это полезно для оптимизации кода, например:
foo_t bar = ...;
short baz;
if (bar > SHORT_MAX)
baz = SHORT_MAX;
else
baz = static_cast<short> (bar);
Это можно заменить на:
foo_t bar = ...;
short baz = ACE_Utils::truncate_cast<short> (bar);
В зависимости от базового типа foo_t, truncate_cast оптимизирует оператор if() целиком, а также диагностику компилятора адреса в результате сравнение подписанных и неподписанных типов. Выбор того, как идти, выполняется во время компиляции через шаблонную метапрограмму.
В идеале, для совместимых типов не требуется такого рода приведение/усечение, но иногда при работе с устаревшими интерфейсами, особенно при низкоуровневых вызовах ОС, иногда не обойти несовместимые типы.
Обратите внимание, что легко злоупотреблять такой литой, поэтому авторы явно заявляют, что она предназначена для внутреннего использования и что приведение не должно использоваться для диагностики компилятора.
Ответ 8
memcpy_cast
является строго стандартным, таким образом безопасным и переносимым альтернативом типа punning:
#include <cstring>
template<typename To, typename From>
inline To memcpy_cast(From x)
{
// Constraints on types from STLSoft union_cast:
// (1) Sizes must be the same.
// (2) Both must be of POD type.
// (3) There must be either a change of const/volatile,
// or a change of type, but not both.
// (4) Both must be non-pointers, or must point to POD types.
//
// Here checking only (1):
STATIC_ASSERT(sizeof (To) == sizeof (From));
To ret;
std::memcpy(&ret, &x, sizeof ret);
return ret;
}
(где STATIC_ASSERT
- некоторый макрос утверждения времени компиляции (или С++ 11 STATIC_ASSERT
), а ограничения связаны с STLSoft union_cast.hpp).
Затем вы можете попробовать такие вещи, как
uint32_t u = 0x1234ABCD;
//float f = *(float*)&u; // unsafe
float f = memcpy_cast<float>(u); // safe
(Здесь другая реализация: dbg memcpy_cast.hpp.)
(Edit: Кроме того, Boost.SIMD имеет bitwise_cast
, который внутренне использует memcpy_cast.)
Ответ 9
Существуют экземпляры операторов каста С++, определенные в Boost.Lambda, которые очень полезны в различных лямбда-выражения из простых:
vector<int> v1; // signed indices
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
vector<size_t> v2(v1.size()); // unsigned +1 incides
std::transform(v1.begin(), v1.end(), v2.begin(),
(boost::lambda::ll_static_cast<size_t>(boost::lambda::_1) + 1));
гораздо сложнее с помощью оператора ll_dynamic_cast
, например, фильтровать объекты определенного (производного) типа в последовательности:
Ответ 10
Visual Studio 6 разрешает rvalues привязываться к регулярным ссылкам (не ошибиться с С++ 0x rvalue references). При переносе в Visual Studio 2003 все изменения, которые наш код зависел от этого нестандартного поведения, должны были быть изменены.
например. Определение
bool get_string(char* buff, int& length)
{
if (myStrLength >= length)
{
length = myStrLength;
return false; // Nobody used exceptions in 1998 :o)
}
strcpy(buff, myStr);
return true;
}
Применение:
char buff[1024];
get_string(buff, sizeof(buff)); // Assumes size is sufficient
Чтобы сделать порт намного быстрее, мы написали следующее lvalue_cast
.
// Danger, only use this on function arguments that will not be stored
template <class T>
T& lvalue_cast(const T& t)
{
return const_cast<T&>(t);
}
Поскольку временная область находится в области до следующей точка последовательности (следующая точка с запятой), а значения r не соответствуют true const
это хорошо определено (по крайней мере, по моему мнению).