我可以在一行上写这个带有变量声明的 if 语句吗?[复制]

Can I write this if statement with a variable declaration on one line? [duplicate]

提问人:Zebrafish 提问时间:9/1/2017 最后编辑:BoannZebrafish 更新时间:12/29/2017 访问量:7714

问:

我想知道有没有办法把它放在一行上?

if (auto r = getGlobalObjectByName(word)) r->doSomething; // This works fine

if (!auto r = getGlobalObjectByName(word)) r->doSomething; // Says "expected an expression"

if (auto r = getGlobalObjectByName(word) == false) r->doSomething; // Also doesn't work.

我还尝试用额外的括号将其包围,但这似乎不起作用。我发现这在一行上做起来真的很方便。

C++ IF语句

评论

24赞 Cory Kramer 9/1/2017
顺便说一句,我不鼓励尝试一行。事实上,我什至不鼓励你在条件中使用赋值的返回值,各种编译器实际上将其视为编译器警告,因为它很容易导致错误。更不用说在这项任务中增加第三步了。首先要确保你的代码是可读的,简洁并不总是更好。
4赞 Peter 9/1/2017
我看不出重点。如果测试为 ,则计算将给出未定义的行为,除非满足一组非常具体的条件(例如,属于某个类类型,该类类型提供 或与 和 相当,并且提供 如果对象测试为rfalser->doSomethingroperator!()booloperator->()false)
6赞 Galik 9/1/2017
@CoryKramer 这不是赋值,而是初始化,没有编译器会为此发出警告。您必须将其与编译器在构造之前声明变量时给出的赋值警告混淆。在这种情况下,它的作用是保护您免于使用赋值而不是等价。但这是不同的,不能混淆。if
3赞 Martin Bonner supports Monica 9/1/2017
在对编译器不会发出警告的评论投了赞成票之后,我仍然认为这种过度简洁通常是一个坏主意。
3赞 phuclv 9/2/2017
auto r = getGlobalObjectByName(word) == false意味 着auto r = (getGlobalObjectByName(word) == false)

答:

52赞 Passer By 9/1/2017 #1

从 C++ 17 开始,您可以使用初始值设定项 if 语句

if (auto r = getGlobalObjectByName(word); !r) r->doSomething;

语义如下:

if (init-statement; condition) statement

与“传统”if 语句的唯一区别是 ,它初始化块作用域中的变量,类似于 for 循环。init-statement

评论

2赞 Zebrafish 9/1/2017
我一直在诅咒语言添加更多功能,但有些东西我挖掘了。谢谢
1赞 Passer By 9/1/2017
@Zindarod 分号后面的表达式的行为与以前一样。
2赞 Bathsheba 9/1/2017
它可能是带有重载运算符的非指针类型。->
1赞 Bathsheba 9/1/2017
但假设其他情况也是不必要的。
1赞 Shamtam 9/1/2017
有什么理由不行吗?if (auto r = !getGlobalObjectByName(word)) r->doSomething;
4赞 Galik 9/1/2017 #2

你要做的很好。通过在 if 中定义变量,限制其作用域。这有助于在杂散变量达到其目的后减少它们的数量。

使用这种技术,如果你想遵循否定路径,你需要使用其他的,如下所示:

if(auto r = getGlobalObjectByName(word))
{
    r->doSomething();
}
else
{
    // r == nullptr
    // so do something else
}
13赞 Martin Bonner supports Monica 9/1/2017 #3

如果您有 C++17,请使用表单。如果没有,您有三个选择:if (init statement; condition)

  • 不要再试图把这一切放在一行上。例如:

    auto r = getGlobalObjectByName(word);
    if (!r) r->doSomething();
    
  • 使用:else

    if (auto r = getGlobalObjectByName(word)) {} else r->doSomething();
    

(请注意,这需要一个智能指针,该函数具有非常奇怪的语义。OTOH,我认为这实际上是一小段示例代码,而不是您的实际代码)。roperator bool()

我想我只会在将所有内容都放在一行非常重要的情况下使用表单(例如,保留代码的表格格式)。else

评论

0赞 Ruslan 9/1/2017
r可能不是智能指针,例如类,它转换为指示失败,并且可以例如在这种情况下提供错误消息。Statusfalse
0赞 Martin Bonner supports Monica 9/1/2017
@Ruslan 是的,它可以,但是对我来说,提供错误消息的重载似乎是操作员重载滥用。 会更干净。operator ->r.Error().doSomething()
0赞 Ruslan 9/1/2017
嗯,是的,确实,没有想到.operator->
4赞 Erroneous 9/2/2017 #4

还有一种方法可以用 lambda 和 C++14 做到这一点,但它看起来确实很愚蠢。

[](auto r){ if(!r)r->doSomething(); }(getGlobalObjectByName(word));

在 C++11 中,你也可以做这个可怕的混乱(同样的想法,只是没有auto)

[](decltype(getGlobalObjectByName(word)) r){ if(!r)r->doSomething(); }(getGlobalObjectByName(word));

它肯定不比Martin Bonner提到的这个更清晰的C++11版本更好:

{
    auto r = getGlobalObjectByName(word);
    if(!r)r->doSomething();
}

在这里,您的代码很清楚,您只想在 if 语句的持续时间内存在。r

评论

0赞 Zebrafish 12/29/2017
我不明白你写的第一行发生了什么。您声明一个 lambda,然后立即在一行上调用该函数,就好像函数的返回值作为参数传递给 lambda 一样。这是 C++14 的功能吗?它叫什么?
0赞 Erroneous 12/29/2017
@Zebrafish C++14 功能是 lambda 的自动参数。该代码只是构造一个 lambda 并立即调用其 operator()。如果我使用类型而不是自动,我可以在 C++11 中完成。
2赞 Ruslan 9/2/2017 #5

在 C++ 17 之前,您可以定义一个包装类,如下所示:

#include <utility>
template<typename T>
class NotT
{
    T t;
public:
    template<typename U>
    NotT(U&& u) : t(std::move(u)) {}
    explicit operator bool() const { return !t; }
    T      & value()       { return t; }
    T const& value() const { return t; }
};
template<typename T> NotT<T> Not(T&& t)
{
    return NotT<T>(std::move(t));
}

#include <memory>
#include <iostream>

int main()
{
    if(auto p=Not(std::make_shared<int>(2134)))
        std::cout << "!p: p=" << p.value().get() << '\n';
    else
        std::cout << "!!p: p=" << p.value().get() << ", *p=" << *p.value() << '\n';

}

Live example