Pull to refresh

Comments 39

UFO just landed and posted this here
В статье приведен тот случай, когда условие нужно выкинуть :)
Будет ли оно компилироваться, если вместо true/false подставить булевую переменную? Под всеми платформами и компиляторами скомпилируется?
Моя платформа Java, там подобная ересь вообще не скопмилируется :)
… написанная на с, cpp и прочей ереси ^_^
UFO just landed and posted this here
Вроде такого:
gtk_list_store_set(GTK_LIST_STORE(model), &iter, VM_TYPE,  strcmp((char *)type, "1") ? "ВМ" : "Шаблон", -1);

не printf, но смысл тот же. Кажется, тернарный оператор для printf приводится как первый пример его использования в книге «Язык С» за авторством K & R. Там как раз множественные числа в тексте так обходились.
В Си нет ссылок, поэтому общий тип, конечно, 'int', а не 'int&', и не работает. Но всё можно «исправить»:
*(condition ? &i : &j) = 45;
Не так красиво, но работает.
Вот это хорошо!

А то я тоже завис на таком извращении.
UFO just landed and posted this here
А за такое в продакшене ТехДиректор по репе не настучит?
Этот топик не о продакшене, а о возможностях тернарного оператора. Я просто привёл «фикс» для примера в топике для Си. Но в принципе, иногда надо бывает выполнить что-то подобное, обычно делается так.
C++:
int& x = (condition ? i : j);
// some code
x = 45;

C/C++:
int* x = (condition ? &i : &j);
// some code
*x = 45;
Да, спорная запись. Заставлять компилятор размещать переменные в памяти, только ради того, чтобы не писать лишний if. И вдобавок, возможно, разрушить оптимизацию (изменив значение неизвестно какой переменной).
Хотя если подобное выражение находится в третьем выражении оператора for (что-нибудь вроде for(a=0,b=n;b-a>1;*(cond? &a: &b)=c){… } для бинарного поиска) — то туда if не вставить.
В данном случае это const char*. Но объект String(«dcba») уничтожится в конце выражения и s будет указывать на невалидную память.


Там проблема не с тернарным оператором а с классом строки.

const char *s = String("Abcd");


будет падать ничуть не хуже.
это аналогично такому

    std::string a = "aaaaaaaaaaa";
    const char* b =  a.c_str();
    a.append("wwwwwwwwwwwwwwwwwwwww"); 
Спасибо за статью. То что тернарный оператор может работать как lvalue знал, а то что это поведение определяется общим типом — нет.

Я бы еще упомянул про опасности связанные с низким приоритетом тернарного оператора, которые с успехом были раскрыты в одном из постов PVS Studio. Ну и про то, что части оператора являются точками следования, тоже стоило сказать.
По поводу четвертого пункта, но почему не «return !true;»? :-)
P.S. Интересно, почему я не могу пользоваться тегом?
Может быть потому что статья про использование тернарного оператора и вместо пары 0 и 1 можно поставить 4 и 9?
Часто пользуетесь в своем коде подобным?
Знать это хорошо, но знание ради знания это онанизм.
Вы не рассказали о самой интересной и малоизвестной фиче тернарного оператора — возможности использовать throw в нем.
int foo() {
  return valid ? some_state : throw std::logic_error();
}


«Оправданность» в данном случае — понятие субъективное. Да, этот код можно написать с if. И он, наверное, будет так понятнее.
Понятно, спасибо. Но с моей точки зрения это нехорошая практика. Выражение должно быть однозначное. Изначальный смысл тернарного оператора — выбор значения (тогда как условной конструкции — выбор ветви выполнения). Здесь же смешались в кучу кони, люди. Да, это компактнее, но стоит ли экономить на одном условии? Ради чего?

По своей сути это ничуть не лучше, чем выражение из недавней статьи про JavaScript:

!~utils.indexOf(adjacency, id) && adjacency.push(id);

которое является способом записи условия:

if (utils.indexOf(adjacency, id) >= 0)
    adjacency.push(id);
Ну и заодно, при разговоре об общих типах, не вредно упомянуть о том, что void является допустимым типом операндов. Т.е. можно писать так:
true ? (void)C() : (void)D();
Багфикс:
class String
{
  public:
  const char* operator() (arguments-of-the-function-operator-must-go-here);
  operator const char*() const; // оператор приведения типа
};

Спасибо, поправил.
class fake_logger
{
public:
    template<class T>
    void operator << (const T& any)
    {}
};

#ifdef BUILD_ON_WINDOWS
    #define LOGGER() (true) ? __noop : fake_logger()
#else
    #define LOGGER() (true) ? void() : fake_logger()
#endif

да макросы плохо, но бывает пользуюсь такой особеностью
чтобы «выключать» логгер в релизе и использовать синтаксис аля для стримов
LOGGER() << «blahblahblah»;

А это точно не будет генерировать код при отключенном логере?
Я так понимаю, это равносильно
if (false) fake_logger() << "blah-blah";
Точно? Не вижу я там «if (false)» Т.е. fake_logger дёргаться и параметры для "<<" него будут готовиться и если там строки, то это тяжёлые операции, которые будут выполнятся даже при отключённом логере.
LOGGER() << "blahblahblah";

Раскроется в:
(true) ? void() : fake_logger() << "blahblahblah";

что благодаря крайне высокому приоритету тернарного оператора эквивалентно:
(true) ? void() : (fake_logger() << "blahblahblah");

Так как до правой части дело не дойдёт, то и вычислений никаких там произведено не будет.
Ох, спасибо. Что-то я протупил.
Чем это лучше, чем
define LOGGER()  fake_logger()
?
в моем случае ещё бывает

LOGGER() << formatted("....", param1, param2,...); 

чтобы избежать вычисления параметров, хотелось бы чтобы этот код вообще не вызывался.
Sign up to leave a comment.

Articles