Является ли это разумным использованием тернарного оператора?[закрыто]
Вопрос
Существуют ли какие-либо проблемы с пониманием / ремонтопригодностью, возникающие в результате использования такого кода, как
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);
Я мог бы оставь это ... В зависимости от моего настроения.;)