将 out/in out 参数与 if/switch 的 init 语句一起使用

Using out/in-out parameters with init-statement for if/switch

本文关键字:out 语句 init 一起 if in 参数 switch      更新时间:2023-10-16

我想使用带有"占位符"或类似内容的 if/switch 的 init 语句,这些参数作为 out-or in-out-parameter 传递给函数,例如。T f(X& x).

例如,std::filesystem中的"全新"C++17 noexcept 重载使用 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

相反,我不能在我的if子句中将其与 init 语句一起使用:

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("或其他东西,因为我正在等待这种情况, 这也不例外。

旁注:我的问题不是讨论使用异常而不是错误代码,反之亦然。

我不确定在描述 if/switch 的 init 语句时是否考虑了我上面描述的情况,或者已经被丢弃了,而至少对我来说,找到一种优雅的方法会很好,没有包装函数处理 out/in-out-参数与 if/switch 的 init 语句。

TL;DR:从 if/switch 的 init-语句中获利与 out/in-out 参数相结合的最优雅方法是什么?可能吗?

问候 FA85

我会发现使用这样的东西(伪代码(很好:

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;
}

我认为你在问自己正确的问题。

init-语句的想法是初始化一个新变量。如果需要多个语句来初始化变量,则可能需要重构代码,而您所做的是一个选项。

所以这里有一个折衷方案:

  • 你想要一些简洁的东西

  • 您希望能够创建一个具有iffor生存期的变量

委员会可能讨论过允许多个语句,但他们决定这会在不影响代码质量的情况下增加可读性(至少这是我的理解(。

不过,小错误:

return std::move(ec);

直接返回ec,而不是按&&,按值。编译器可能会告诉你很多。