使用 static_cast<>() 声明变量时使用 auto 的目的是什么?

what is the purpose of using auto when declaring variable with static_cast<>()?

Scott Meyers 在他的 "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14" 中讨论了 auto 在不同情况下的使用。他指出,有时 auto 会扣除不正确的类型,因为通常情况下,开发人员会(可能无意识地)使用隐式转换,如下所示:

std::vector<bool> vec = {true, false};
bool a = vec[0]

像这样使用 auto 时:

std::vector<bool> vec = {true, false};
auto a = vec[0]

会使 a 变成 std::vector<bool>::reference 所以作者建议在赋值之前转换它:

auto a = static_cast<bool>(vec[0])

我不明白如果我可以简单地依靠隐式转换,为什么我会以这种方式使用 autostatic_cast<>。我认为这可能是为了强调正在进行转换的事实(明确指出),但对我来说它仍然看起来有点矫枉过正。这种解决方案的优势是什么?

干杯!

在许多情况下,static_cast 可以帮助减少编译器的歧义,也可以帮助减少 reader 与 auto 一起使用时的歧义。

具体来说,在考虑例子中的std::vector<bool>时,我可以想到两种这样的情况:

  • 如果将其传递给推导参数的函数模板,您可能宁愿 decltype(a) 成为 bool 而不是某种 std::__vector_bool_reference_wrapper 类型,或者
  • 如果将 a 传递给具有接受 bool&bool* 的签名的函数,您会希望它是正确的类型——因为隐式转换不会发生

上述情况适用于任何可能具有隐式转换的事物,而不仅仅是 bool/reference-wrapper。

此外,其他类型的案件会变得更有趣。

指针:

nullptr 属于 decltype(nullptr) 类型,也称为 std::nullptr_t。这是不是任何类型T*。这意味着您不能编写如下代码:

auto p = nullptr;
...
p = &a; // compile-error!

而是需要:

auto p = static_cast<T*>(nullptr);
...
p = &a; // works!

无意促销

无意的推广会产生意想不到的错误。如果代码使用 auto 并且依赖于整数溢出,而不明确类型,则可能会无意中产生整数提升——这可以防止溢出发生。这可能会在某些代码中产生细微的错误。

float/double 晋升也可能发生同样的情况,尽管我很难想到这种情况会是负面的。

如果将结果传递给一个函数,该函数期望引用或指向 T 的类型与提升类型不同(类似于 vector<bool>上面的例子)。


我确定存在其他情况,其中 static_cast 在与 auto 一起使用时很重要,但一般来说,想到的情况涉及重载决策和模板附带的严格类型类型推导。

除此之外,在某些情况下,static_cast 只是有助于使用大量 auto

的复杂函数的一般可读性

以此为前提:你要尽量用auto,那么

std::vector<bool> vec = {true, false};
auto a = vec[0];

会做出意想不到的事情,同时:

auto a = static_cast<bool>(vec[0]);

会给你一个bool。我认为这里的重点不是说:"Use static_cast together with auto as much as possible"。而是:"If you want to use auto almost always, then sometimes you have to add a static_cast"

...in this way if I can simply count on implicit conversion...

在这种情况下是的,但是您没有使用 auto。还要考虑不是您使用 auto 的情况。假设您得到一个定义为:

的 lambda
auto foo = [](auto x){};

您无法更改其签名,因此如果您想使用 vec[0] 调用它并推导出正确的类型,则需要使用 static_cast

还要考虑,如果您遵循 AAA(几乎总是自动),那么您确实希望将所有变量声明为 auto。这有助于永远不会忘记初始化变量。如果你回到

bool a = vec[0];

当需要时,这就打败了首先应用 AAA 的全部意义。您希望保持一致,而不是根据初始化程序选择 auto 与显式类型。