Является ли это разумным использованием тернарного оператора?[закрыто]

StackOverflow https://stackoverflow.com/questions/522919

  •  22-08-2019
  •  | 
  •  

Вопрос

Существуют ли какие-либо проблемы с пониманием / ремонтопригодностью, возникающие в результате использования такого кода, как

inVar1 == 0 ? NULL : v.push_back(inVar1);
inVar2 == 0 ? NULL : v.push_back(inVar2);

и так далее.

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

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

Это было полезно?

Решение

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

if (inVar != 0)
  v.push_back(inVar);

Мне пришлось несколько раз просмотреть ваш пример, чтобы с какой-либо уверенностью определить, каков будет результат.Я бы даже предпочел однострочный оператор if() {}, чем ваш пример - и я ненавижу однострочные операторы if :)

Другие советы

Троичный оператор предназначен для возврата значения.

ИМО, это не должно изменять состояние, и следует использовать возвращаемое значение.

В другом случае используйте операторы if.Инструкции If предназначены для выполнения блоков кода.

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

Однако то, что вы здесь делаете, подрывает доверие к нему.Это короче, да, но это излишне сложно.

Я думаю, этого следует избегать.Вы могли бы использовать вместо него оператор if из 1 строки.

if(inVar1 != 0) v.push_back(inVar1);

Компиляторы в наши дни будут выполнять if так же быстро, как и троичный оператор.

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

Я голосую за

if ( inVar != 0 )
{
   v.push_back( inVar );
}

почему скобки...потому что однажды вы, возможно, захотите вставить туда что-то еще, и скобки будут сделаны заранее для вас.Большинство редакторов в наши дни все равно вставят их.

Ваше использование троичного оператора вам ничего не даст, и вы ухудшите читаемость кода.

Поскольку троичный оператор возвращает значение, которое вы не используете, это нечетный код.Использование if это гораздо более понятно в случае, подобном вашему.

Как упоминал litb в комментариях, это недопустимый C ++.GCC, например, выдаст ошибку в этом коде:

error: `(&v)->std::vector<_Tp, _Alloc>::push_back [with _Tp = int, _Alloc =
std::allocator<int>](((const int&)((const int*)(&inVar1))))' has type `void' 
and is not a throw-expression

Однако это можно обойти, приведя:

inVar1 == 0 ? (void)0 : v.push_back(inVar1);
inVar2 == 0 ? (void)0 : v.push_back(inVar2);

Но какой ценой?И с какой целью?

Не похоже, что использование троичного оператора здесь более лаконично, чем оператор if в этой ситуации:

inVar1 == 0 ? NULL : v.push_back(inVar1);
if(inVar1 != 0) v.push_back(inVar1);

Хотя на практике я согласен с мнением тех, кто не одобряет этот тип написания (при чтении вам приходится проделывать дополнительную работу по сканированию выражения на предмет его побочных эффектов), я хотел бы предложить

!inVar1 ?: v.push_back(inVar1);
!inVar2 ?: v.push_back(inVar2);

...если вы предпочитаете малоизвестное, то есть.GCC позволяет x ?: y на месте x ? x : y. :-)

Я использую тернарный оператор, когда мне нужно вызвать какую-то функцию с условными аргументами - в данном случае это лучше, чем if.

Сравнить:

printf("%s while executing SQL: %s",
        is_sql_err() ? "Error" : "Warning", sql_msg());

с

if (is_sql_err())
    printf("Error while executing SQL: %s", sql_msg());
else
    printf("Warning while executing SQL: %s", sql_msg());

Я нахожу первое более привлекательным.И это соответствует СУХОЙ принцип, в отличие от последнего - вам не нужно писать две почти одинаковые строки.

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

if (inVar != 0) {
    v.push_back(inVar);
}

Я думаю, что иногда троичные являются необходимым злом в списках инициализаторов для конструкторов.Я использую их в основном для конструкторов, где я хочу выделить память и установить некоторый указатель, указывающий на нее перед телом конструктора.

Пример, предположим, у вас есть класс хранения целых чисел, который вы хотели бы заставить принимать вектор в качестве входных данных, но внутреннее представление представляет собой массив:

class foo
{
public:
    foo(std::vector<int> input);
private:
    int* array;
    unsigned int size;
};

foo:foo(std::vector<int> input):size(input.size()), array( (input.size()==0)?
        NULL : new int[input.size])
{
    //code to copy elements and do other start up goes here
}

Вот как я использую троичный оператор.Я не думаю, что это так сбивает с толку, как некоторых людей, но я действительно думаю, что следует ограничить то, как часто они этим пользуются.

Большинство замученных троичных (как вам такая аллитерация?) Я вижу, что это просто попытки поместить логику, которая действительно принадлежит оператору if, в место, где оператор if не принадлежит или не может находиться.

Например:

if (inVar1 != 0)
  v.push_back(inVar1);
if (inVar2 != 0)
  v.push_back(inVar2);

работает при условии, что v.push_back является недействительным, но что, если он возвращает значение, которое необходимо передать другой функции?В таком случае это должно было бы выглядеть примерно так:

SomeType st;
if (inVar1 != 0)
  st = v.push_back(inVar1);
else if (inVar2 != 0)
  st = v.push_back(inVar2);
SomeFunc(st);

Но это еще не все, что нужно переварить для такого простого фрагмента кода.Мое решение:определите другую функцию.

SomeType GetST(V v, int inVar1, int inVar2){
    if (inVar1 != 0)
      return v.push_back(inVar1);
    if (inVar2 != 0)
      return v.push_back(inVar2);        
}

//elsewhere
SomeFunc(GetST(V v, inVar1, inVar2));

Во всяком случае, дело вот в чем:если у вас есть какая-то логика, которая слишком замучена для троичного кода, но будет загромождать ваш код, если ее поместить в оператор if, поместите ее куда-нибудь еще!

inVar1 != 0 || v.push_back(inVar1);
inVar2 != 0 || v.push_back(inVar2);

распространенный шаблон, встречающийся в таких языках, как Perl.

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

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

if (cond) doIt();

cond ? noop() : doIt();

Принимая во внимание, что следующее гораздо приятнее выполнить пошагово (независимо от того, есть у вас фигурные скобки или нет).:

if (cond) {
    doIt();
}

Как уже упоминалось, это не короче и не понятнее, чем оператор if в 1 строку.Тем не менее, это также больше не так - и на самом деле не так уж сложно грокнуть.Если вы знаете троичный оператор, то довольно очевидно, что происходит.

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

var2 = inVar1 == 0 ? NULL : v.push_back(inVar1);

Тот факт, что троичный оператор всегда возвращает значение - IMO - не имеет значения.Конечно, нет требования, чтобы вы использовали все возвращаемые значения ... в конце концов, присваивание возвращает значение.

При этом я бы заменил его на оператор if, если бы наткнулся на него с нулевой ветвью.

Но, если он заменил 3 - строчный оператор if:

if (inVar == 0) {
   v.doThingOne(1);
} else {
   v.doThingTwo(2);
}

с:

invar1 == 0 ? v.doThingOne(1) : v.doThingTwo(2);

Я мог бы оставь это ... В зависимости от моего настроения.;)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top