Использование параметров out / in-out с оператором init для if / switch - PullRequest
0 голосов
/ 20 октября 2018

Меня интересует использование оператора init для if / switch с "заполнителями" или чем-то в этом роде, которые передаются в функцию в качестве параметров out или in-out, например, T f(X& x).

Например, «совершенно новый» C ++ 17 noexcept overloads в std::filesystem использует параметр out для передачи std::error_code по ссылке:

bool create_directory(const std::filesystem::path& p,
                      const std::filesystem::path& existing_p,
                      std::error_code& ec) noexcept;

или

void rename(const std::filesystem::path& old_p,
            const std::filesystem::path& new_p,
            std::error_code& ec) noexcept;

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

namespace fs = std::filesystem;
if (auto ec = fs::rename(old_p, new_p); ec)
{
    // use ec handle error
} // ec's lifetime ends

Вместо этого я не могу использовать его с оператором init в моем if предложение:

namespace fs = std::filesystem;
std::error_code ec;
fs::rename(old_p, new_p, ec);
if (ec)
{
    // handle error
}
// ec is still there

На самом деле, я работаю над этим, используя обертку для поддержания кода вызывающего абонента в чистоте:

namespace fs = std::filesystem;
inline std::error_code wrap_rename_ec(const fs::path& old_p,
                                        const fs::path& new_p) noexcept
{
    std::error_code ec;
    fs::rename(old_p, new_p, ec);
    return ec;
}

int main (void)
{
    if (auto ec = wrap_rename_ec("old_p", "new_p"); ec)
    {
         // use ec to handle error
    } // ec's lifetime ends
}

Мне бы хотелось использовать что-то подобное (псевдокод):

namespace fs = std::filesystem;
if (std::error_code ec, fs::rename(old_p, new_p, ec); ec)
{
    // handle error
} // ec's lifetime ends

Я мог бы использовать другую перегрузку std::filesystem::rename, которая выдает filesystem_error при ошибке, но передача существующего пути или имени файла в качестве нового пути или имени файла не должна бытьошибка, которая должна нарушить поток управления в программе, я, возможно, хочу добавить "(2) "или что-то, потому что я жду этого случая, который не является исключением.

Примечание: мой вопрос направлен не на обсуждение использования исключений по кодам ошибок или наоборот.; -)

Я не уверен, были ли учтены случаи, подобные описанному выше, при описании выражений init для if / switch или были отброшены, хотя по крайней мере для меня это было быприятно найти элегантный способ без функций-оболочек, обрабатывающих параметры out / in-out с помощью операторов init для if / switch.

TL; DR: Какой самый элегантный способ получения прибыли от операторов init дляПереключатель if / в сочетании с параметрами out / in-out?Возможно ли это вообще?

С уважением, FA85

Ответы [ 2 ]

0 голосов
/ 20 октября 2018

Было бы неплохо использовать что-то вроде этого (псевдокод):

using fs = std::filesystem;
if (std::error_code ec, fs::rename(old_p, new_p, ec); ec)
{
    // handle error
} // ec's lifetime ends

Вы почти угадали правильный синтаксис.

Выищем это:

using fs = std::filesystem;
if (std::error_code ec; fs::rename(old_p, new_p, ec), ec) // Note that , and ; were switched.
{
    // ...
}

Также обратите внимание, что использование вашей обертки вызывает UB из-за доступа к висячей ссылке:

using fs = std::filesystem;
inline std::error_code&& wrap_rename_ec(const fs::path& old_p,
                                        const fs::path& new_p) noexcept
{
    std::error_code ec; // Lifetime of `ec` ends after the function returns.
    fs::rename(old_p, new_p, ec);
    return std::move(ec);
}

Правильный способ записи, который будет:

using fs = std::filesystem;
std::error_code wrap_rename_ec(const fs::path& old_p,
                                        const fs::path& new_p) noexcept
{
    std::error_code ec;
    fs::rename(old_p, new_p, ec);
    return ec;
}
0 голосов
/ 20 октября 2018

Я думаю, что вы задаете себе правильные вопросы.

Идея оператора init состоит в инициализации новой переменной.Если вам нужно более одного оператора для инициализации переменной, то вы, вероятно, захотите провести рефакторинг кода, и вы сделали один вариант.

Итак, здесь есть компромисс:

  • вы хотите что-то сжатое

  • вы хотите иметь возможность создать одну переменную со временем жизни if или for

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

И все же небольшая ошибка:

return std::move(ec);

Возврат ec напрямую, а не &&, по значению.Компилятор, вероятно, скажет вам столько же.

...