Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Секреты тернарного оператора

Anna | 24.06.2014 | нет комментариев
Каждый уважающий себя программист С\С знает что такое тернарный оператор и множество использовало его правда бы раз в своих программах. Но знаете ли вы все секреты тернарного оператора? Какие потенциальные угрозы сопряжены с его применением и какие, казалось бы не связанные с его прямым предназначением, вероятности в нем таятся? Эта статья дает вам вероятность проверить свои познания и, допустимо, узнать что-то новое.
Начнем с небольшого теста.

Тест

Скомпилируется ли дальнейший код? Объясните отчего.
1.

int i;
int j;
(false ? i: j) = 45;

2.

int i;
int j;
(true ? i: j) = 45;

3.

short i;
int j;
(true ? i: j) = 45;

4.

return true ? 0 : 1;

5.

true ? return 0 : return 1;

Какой будет итог у дальнейшего ломтика? Отчего?
6.

std::cout << (false ? 9 : '9') << " " << (true ? 9 : '9');

Какие значения будут у переменных a, b и c в итоге выполнения дальнейшего кода? Отчего?
7.

int a = 1;
int b = 1;
int c = 1;
a = true ?   b :   c;

8. Назовите обстановку, где невозможно применять if{…} else{…}, но дозволено тернарный оператор.
9. Какие потенциальные угрозы скрываются в применении тернарного оператора? В чем их повод?
10. Какие непредвиденные применения тернарного оператора приходят вам в голову?

Трактование

Выходит, начнем. Тернарный оператор выдается из ряда других операторов в С . Его называют “conditional expression“. Ну а так как это expression, выражение, то как у всякого выражения, у него должен быть тип иvalue category. Собственно, ответив на вопросы какой тип и value category у тернарных операторов в всяком из первых семи вопросов теста, мы легко решим поставленные задачи.

Тут начинается самое увлекательное. Оказывается типом тернарного оператора будет особенно всеобщий тип его 2-х последних операндов. Что значит особенно всеобщий? Это легче каждого пояснить на примерах. У intи short всеобщим типом будет int.
У A и B в дальнейшем фрагменте всеобщим типом будет также int.

struct A{ operator int(){ return 1; } };
struct B{ operator int(){ return 3; } };

Т.е. особенно всеобщий тип это такой тип, к которому могу быть приведены оба операнда. Абсолютно могут быть обстановки, когда всеобщего типа нет. Скажем у

struct C{};
struct D{};

всеобщего типа нет, и дальнейший фрагмент вообще не скомпилируется

(true ? C() : D());

Так. С типом тернарного оператора мы немножко разобрались. Осталось решить вопрос с value category. Здесь действует следующее правило: если в тернарном операторе происходит реформирование типов кособенно всеобщему, то тернарный оператор — rvalue. Если же нет, то lvalue. Сейчас когда мы знаем то, что мы знаем, мы легко ответим на первые 7 вопросов.

Результаты

1. и 2. — Да. Реформирования типов не происходит, а lvalue абсолютно дозволено присваивать значение.
3. — Нет. Тут происходит реформирование типов. Значит value category у выражения слева от знака “=” —rvalue. А rvalue, как вестимо, невозможно присваивать.
4. — Да. Все мы так делали не раз.
5. — Нет. Тут все дело в том, что в С statement не может разбивать expression.
6. Программа выведет «57 9». В данном фрагменте из-за того, что 2ой и 3ий операнд имеют различные типы, происходит реформирование к особенно всеобщему типу. В данном случае int. А ’9′, как вестимо, имеет ASCII код 57.
7. В этом вопросе кроется еще одна специфика тернарного оператора. А именно, вычисляется только тот операнд из второго и третьего, до которого доходит поток выполнения. Однако такое же поведение дозволено следить у if{…}else{…}. Соответственно, значения переменных a, b и с будут 2, 2, 1.

Где невозможно применять if{…} else{…}, но дозволено тернарный оператор?

Скажем, в списке инициализации конструктора. Вы не может написать так:

struct S 
{
	S() : if(true) i_(1) else i_(0){}
	int i_;
};

Но абсолютно дозволено вот так:

struct S 
{
	S() : i_(some_condition ? 0 : 1){}
	int i_;
};

При инициализации ссылки в зависимости от данные. Как вестимо, невозможно объявлять не инициализированную ссылку, следственно дальнейший фрагмент не скомпилируется:

int a = 3;
int b = 4;
int& i;
if(some_condition)
  i = a;
else
  i = b;

А вот дальнейший скомпилируется удачно:

int& i = (some_condition ? a : b);

В С 11 тернарный оператор используется значительно Почаще. Связано это с тем, что в constexpr функциях не должно быть ничего помимо return `expression`. А `expression` абсолютно может представлять из себя тернарный оператор.
В качестве примера приведу типичный алгорифм определения простоты числа

constexpr bool check_if_prime_impl(unsigned int num, unsigned int d)
{
  return (d * d > num) ? true : 
    (num % d == 0) ? false : 
      check_if_prime_impl(num, d   1);
}

constexpr bool check_if_prime(unsigned int num)
{
  return (num <= 1) ? false : 
    check_if_prime_impl(num, 2);
}

В этом же примере, кстати, видно применение каскадных тернарных операторов, которые могут быть неограниченной вложенности и заменять собой множественные if{…} else{…}.

Угрозы тернарного оператора

Возможен у нас есть класс String

class String
{
  public:
  const char* operator();
};

И применять мы его можем, скажем, так:

const char* s = some_condition ? "abcd" : String("dcba");

Как нам теснее вестимо, 2-й и 3-й операнд тернарного оператора приводятся к особенно всеобщему типу. В данном случае это const char*. Но объект String(«dcba») уничтожится в конце выражения и s будет указывать на невалидную память. В лучшем случае программа упадет при попытке в последующем применятьs. В худшем будет выдавать неверные итоги, вызывая негодование у клиента и головную боль у программиста.

«Странное» применение тернарного оператора

Тернарный оператор дозволено применять для определения всеобщего типа 2-х и больше типов. А это, в свою очередь, дозволено применять, скажем, для определения приводится ли один тип к иному.

template <typename T, typename U>
struct common_type
{
	typedef decltype(true ? std::declval<T>() : std::declval<U>()) type;
};

template<typename T, typename U>
struct is_same{ enum { value = false; } };

template<typename T>
struct is_same<T, T>{ enum { value = true; } };
int main() 
{
  std::cout << is_same<int, common_type<A, B>::type>::value <<std::endl;
}

На самом деле, если знать свойства тернарного оператора, такое применение фактически навязывается само собой. Странным тут, вероятно является лишь то, что он применяется не по прямому назначению, т.е. не для выбора одного значения из 2-х в зависимости от данные.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB