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

Обеспечение безопасности auto_cast

GMan опубликовал код для delicious auto_cast "operator" , который позволяет писать код, например, следующий в С++:

float f = 4.0f;
int i = auto_cast(f);
// instead of:
int j = static_cast<int>(f);

или, более ярко,

T x = value;
typename nested_type<with, template_arguments>::type y = auto_cast(x);
// instead of
typedef typename nested_type<with, template_arguments>::type my_type;
my_type z = static_cast<my_type>(x);

В принципе, оператор отлично справляется с удалением ненужной избыточности из static_cast и в то же время по-прежнему безопасен. Его еще более безопасно, чем static_cast, поскольку он предотвращает случайное несоответствие типов:

int i = 1234;
short s = static_cast<char>(i); // s == -46, not 1234!

Однако j_random_hacker заметил недостаток оператора:

static_cast позволяет downcasts, которые являются потенциально опасными.

Действительно, auto_cast должен, вероятно, запрещать downcasts, потому что они могут выйти из строя:

class base { };
class derived : public base { };

base b;
derived* pd = auto_cast(&b); // should fail at compile time.

Отсюда мой вопрос:

Как бы вы изменили реализацию auto_cast, чтобы запретить downcasts? Вероятно, это будет enable_if. Мне особенно интересно решение, позволяющее компилятору обеспечить хорошую диагностику в случае сбоя (= читаемые сообщения об ошибках).

4b9b3361

Ответ 1

Кажется, вы хотите использовать форму инициализации T{u}.

template <typename U>
operator U()
{
    return U{std::forward<T>(mX)};
}

Одна из причин такой равномерной инициализации заключалась в том, что для использования явных конструкторов для создания временного, вам нужно сделать aka T(u). С T{u} эта проблема была решена. Для С++ 03, я думаю, вы могли бы сделать что-то вроде этого:

template<typename T>
struct construct_explicit {
  template<typename U>
  construct_explicit(U &u):t(u) { }
  template<typename U>
  construct_explicit(U const &u):t(u) { }

  T &get() { return t; }
  T const& get() const { return t; }

  T t;
};

Тогда вы можете сказать construct_explicit<U>(mX).get(), хотя в случае, например, в вашей функции преобразования, он также работает, чтобы использовать именованную переменную в качестве промежуточного шага, я думаю,

template <typename U>
operator U()
{
    // or C++03: U u(mX);
    U u(std::forward<T>(mX));
    return u;
}

Ответ 2

Вы можете использовать признаки типа, чтобы отключить оператор, если T является базой R. Поскольку мы находимся в С++ 0x, вы можете явно static_assert(std::is_base_of<T, U>::value, "Cannot auto_cast downwards!");

Ответ 3

Я бы даже не использовал auto_cast, потому что static_cast, const_cast, dynamic_cast и reinterpret_cast также сделаны уродливыми по дизайну, чтобы помочь указывать код, который может потребоваться рефакторинг: уродливая операция должна иметь уродливый вид.

Вторая причина для введения стиль нового стиля заключался в том, что C-style casts очень сложно обнаружить в программе. Например, вы не можете удобно поиск отливок с использованием обычного редактор или текстовый редактор. Эта почти невидимость приведения в стиле C особенно несчастны, потому что они настолько потенциально опасны. Уродливые операция должна быть уродливой синтаксическая форма. Это наблюдение было часть причины выбора синтаксис для приведения нового стиля. дальнейшая причина заключалась в новом стиле бросает, чтобы соответствовать нотации шаблона, так что программисты могут писать свои собственные броски, особенно проверенные временем выполнения слепки.

http://www2.research.att.com/~bs/bs_faq2.html#static-cast

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