Я читал, что в С++ 17 мы можем инициализировать переменные в операторах if, подобных этому
if (int length = 2; length == 2)
//execute something
Вместо
int length = 2;
if (length == 2)
//do something
Несмотря на то, что он короче, он влияет на удобочитаемость кода (особенно для людей, которые не знакомы с этой новой функцией), что, я полагаю, является плохой практикой кодирования для разработки крупного программного обеспечения.
Есть ли какое-либо преимущество использования этой функции, кроме сокращения кода?
Сначала это может быть новым, но мне нравится, что он ясно показывает, что переменная используется только в этой области, а не для остальной части функции.
Я думаю, кто-то сказал несколько лет назад: «Я читал, что в C++ 11 мы можем создавать лямбда-операторы, подобные этому (...) Несмотря на то, что он короче, он влияет на читаемость кода (особенно для людей, которые не знают эта новая функция), что, я полагаю, является плохой практикой кодирования для разработки крупного программного обеспечения».
Я бы сказал, что он имеет точно такую же длину, а не короче.
чистое мнение, следовательно, не ответ: if (int length = 2; length == 2) может быть удивительно, что вы видите это в первый раз, но в этом нет ничего сложного, что можно было бы понять, поэтому уже во второй раз это уже не будет большим сюрпризом и декларирование вещей в том объеме, в котором они принадлежат является одним из основных факторов, влияющих на читабельность. Имхо, ваше предположение неверно ;)
Беспокойство о читаемости кода для людей, которые не знают языка, на котором написан код (что означает «не знаю эту новую функцию»), — это гонка на выживание.
Комментарий @another-dave R2RT был не только о людях, которые не знают эту функцию, это был просто акцент в скобках. Читабельность кода зависит не только от знания языка.
Они действительно должны довести это до логического завершения и позволить всем for, while, dowhile, if и switch иметь те же 3 части, что и текущий for. Первая часть определяет переменные, вторая часть — это фактическое условие (или значение для switch), последняя часть будет выполняться после тела оператора (если только break не используется для выхода из него или условие ложно). Просто для консистенции.
Но while' — это просто средняя часть for. Если вы хотите больше, чем средняя часть, напишите for вместо while . Какой смысл делать их одинаковыми, кроме ключевого слова? (Кстати, я думаю, что «go» полностью избавляется от циклов while, исходя из этой предпосылки)
@another-dave согласился на гонку на выживание. Я не могу получить ?: через код-ревью... :(
Вы должны позволить мне сделать обзор вашего кода... Я часто предлагаю заменить операторы if-else одним безусловным оператором, содержащим условное выражение; это ИМО «легче читать», если более длинная форма содержит много повторяющегося текста. В любом случае, почти все «легче читаемые» позиции являются мнениями, а не эмпирическими фактами. Моя позиция: «будьте краткими, не путайтесь в коде».





Is there any advantage of using this feature other than making the code shorter?
Вы уменьшаете область видимости переменных. Это имеет смысл и повышает удобочитаемость, поскольку усиливает локальность идентификаторов, о которых вам нужно рассуждать. Я согласен с тем, что длинных операторов инициализации внутри операторов if следует избегать, но для коротких вещей это нормально.
Обратите внимание, что вы уже можете выполнять инициализацию и ветвление результата в версиях до C++17:
int *get(); // returns nullptr under some condition
if (int *ptr = get())
doStuff();
Это зависит от личного мнения, но вы можете считать явное условие более читаемым:
if (int *ptr = get(); ptr != nullptr)
doStuff();
Кроме того, возражать против удобочитаемости функции, ссылаясь на то, что люди к ней не привыкли, опасно. В какой-то момент люди не привыкли к умным указателям, но все же сегодня мы все согласны (я думаю), что это хорошо, что они есть.
вы можете использовать if (auto p =get ()), так как оператор bool определен
Это ограничивает область применения length только if. Таким образом, вы получаете те же преимущества, что и мы, когда нам разрешили писать.
for(int i = 0; i < ... ; ++i) {
// ...
}
Вместо утечки переменной
int i;
for(i = 0; i < ... ; ++i) {
// ...
}
Короткоживущие переменные лучше по нескольким причинам. Но назову пару:
Чем короче что-то живет, тем меньше вещей вам нужно помнить при чтении несвязанных строк кода. Если i не существует вне цикла или оператора if, то нам не нужно обращать внимание на его значение вне их. Нам также не нужно беспокоиться, что его значение будет взаимодействовать с другими частями программы, которые находятся за пределами его предполагаемой области (что может произойти, если i выше повторно используется в другом цикле). Коду легче следовать и рассуждать.
Если переменная содержит ресурс, то этот ресурс теперь удерживается в течение кратчайшего возможного периода. И это без лишних фигурных скобок. Также стало ясно, что ресурс связан только с if. Считайте это мотивирующим примером
if (std::lock_guard _(mtx); guarded_thing.is_ready()) {
}
Если ваши коллеги не знают об этой функции, научите их! Умиротворение программистов, которые не хотят учиться, — плохой предлог, чтобы избегать фич.
Я возьму это последнее предложение и наклею его на двухметровый постер.
Кроме того, недолговечные переменные (с узкой областью действия) должны уменьшить количество ошибок, потому что вы не можете случайно повторно использовать переменную позже в коде, как только ее цель будет достигнута.
Или со слабым указателем: if (auto p = ptr.lock(); p && p->foo()) bar(*p);
3. Компилятору разрешено повторно использовать пространство стека в большем количестве случаев. (Если вы когда-нибудь передадите i по ссылке или указателю на внешнюю функцию, например.)
Лучше задать вопрос «в чем преимущество этого перед {int i = 2; if (i == 2) {...}}» (обратите внимание на дополнительную область действия).
Новая форма оператора if имеет множество применений.
Currently, the initializer is either declared before the statement and leaked into the ambient scope, or an explicit scope is used. With the new form, such code can be written more compactly, and the improved scope control makes some erstwhile error-prone constructions a bit more robust.
Открытое стандартное предложение для оператора If с инициализатором
Таким образом, это утверждение упрощает распространенные шаблоны кода и помогает пользователям ограничивать области видимости.
Я надеюсь, что это помогает!
Не могли бы вы уточнить, что вы цитируете предложение? Особенно второй абзац. Я предлагаю блок-кавычки.
Спасибо @StoryTeller, да, я процитировал второй абзац из предложения open-std, включенного в C++17.
В интересах минимизации области действия переменных существует идиома, которая определяет ресурс, только если он действителен при создании (например, объекты файлового потока):
if (auto file = std::ifstream("filename"))
{
// use file here
}
else
{
// complain about errors here
}
// The identifier `file` does not pollute the wider scope
Иногда вы хотите иметь возможность изменить логику этого теста, чтобы сделать отказ первичным предложением, а действительный ресурс — предложением else. Раньше это было невозможно. Но теперь мы можем сделать:
if (auto file = std::ifstream("filename"); !file)
{
// complain about errors here
}
else
{
// use file here
}
Примером может быть исключение:
if (auto file = std::ifstream(filename); !file)
throw std::runtime_error(std::strerror(errno));
else
{
// use file here
}
Некоторым людям нравится кодировать так, чтобы функция выдавала прервать досрочно при ошибке и в противном случае выполнялась. Эта идиома ставит логику прерывания физически выше логики продолжения, что некоторым людям может показаться более естественным.
Это особенно полезно для логических событий. Рассмотрим этот пример:
char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
std::cerr << "bad stuff\n";
}
Кажется немного грубым. Если вы не очень хорошо знакомы с OR, AND с отрицаниями, вам, возможно, придется сделать паузу и подумать об этой логике, которая, как правило, является плохим дизайном. С помощью if-initialization вы можете добавить выразительности.
char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
std::cerr << "bad stuff\n";
}
именованная переменная также может быть повторно использована внутри if. Например:
if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
std::cerr << distance << " is too small\n";
}
Это здорово, особенно если учесть, что переменная ограничена областью видимости и, следовательно, не загрязняет пространство впоследствии.
Я понимаю, что это субъективно, но я настоятельно предпочитаю вашу "грубую" версию той, где используется if-initializer. Мне так легче читать и понимать.
@FabioTurati Я полагаю, это потому, что вы хорошо с ней знакомы, а другая версия новая. Но со временем я ожидаю, что if-initializer переиграет что-нибудь подобное.
Это расширение существующей функции, которая, по моему опыту, облегчает чтение.
if (auto* ptr = get_something()) {
}
Здесь мы оба создаем переменную ptr и проверяем, не является ли она нулевой. Область применения ptr ограничена тем, где она действительна. Гораздо проще убедить себя, что любое использование ptr допустимо.
Но что, если мы говорим о чем-то, что не конвертируется в bool таким образом?
if (auto itr = find(bob)) {
}
Это не работает. Но с этой новой функцией мы можем:
if (auto itr = find(bob); itr != end()) {
}
Добавьте пункт, говорящий «когда эта инициализация действительна».
По сути, это дает нам набор токенов, которые означают «инициализировать какое-то выражение и, когда оно допустимо, выполнить некоторый код. Если оно недействительно, отбросить его».
Еще со времен C++98 трюк с проверкой указателя стал идиоматическим. Как только вы примете это, расширение станет естественным.
Кроме масштаба?