【问题标题】:Can I write this if statement with a variable declaration on one line? [duplicate]我可以在一行上写一个带有变量声明的 if 语句吗? [复制]
【发布时间】:2024-05-03 13:55:02
【问题描述】:

我想知道是否有办法把它放在一条线上?

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.

我也试过用额外的括号括起来,但这似乎不起作用。我发现这在一条线上真的很方便。

【问题讨论】:

  • 作为旁注,我不鼓励尝试单行。事实上,我什至不鼓励您在条件various compilers actually treat that as a compiler warning 中使用赋值的返回值,因为它很容易导致错误。更不用说在该任务中添加第三步了。首先确保你的代码是可读的,简洁并不总是更好。
  • 我看不懂重点。如果r 测试为false,则评估r->doSomething 将给出未定义的行为,除非满足一组非常具体的条件(例如r 属于某个类类型,该类类型提供operator!() 或与bool 相媲美,并提供operator->(),如果对象测试为false,则该operator->() 不会产生空指针)
  • @CoryKramer 这不是赋值,而是初始化,没有编译器会为此发出警告。当变量在 if 构造之前声明时,您必须将其与编译器给出的赋值警告混淆。在这种情况下,它可以保护您不使用分配而不是等价。但这是不同的,不能混淆。
  • 赞成编译器不会警告的评论,我仍然认为这种过度简洁通常是一个坏主意。
  • auto r = getGlobalObjectByName(word) == false 意思是auto r = (getGlobalObjectByName(word) == false)

标签: c++ if-statement


【解决方案1】:

还有一种方法可以用 lambdas 和 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();
}

您的代码清楚地表明您希望 r 仅在 if 语句的持续时间内存在。

【讨论】:

  • 我不明白你写的第一行发生了什么。您声明一个 lambda,然后在一行中立即调用一个函数,就好像该函数的返回值作为参数传递给 lambda。这是 C++14 的一个特性吗?它叫什么?
  • @Zebrafish C++14 特性是 lambdas 的自动参数。代码只是构造一个 lambda 并立即调用它的 operator()。如果我使用类型而不是 auto,我可以在 C++11 中做到这一点。
【解决方案2】:

从 C++17 开始,您可以使用 initializer if-statement:

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

语义是:

if (init-statement; condition) statement

与“传统” if 语句的唯一区别是 init-statement,它在块范围内初始化一个变量,类似于 for 循环。

【讨论】:

  • 我一直在诅咒语言添加更多功能,但有些东西我很喜欢。谢谢
  • @Zindarod 分号后面的表达式的行为与之前一样。
  • 它可能是带有重载-> 运算符的非指针类型。
  • 但是另外假设也是没有必要的。
  • if (auto r = !getGlobalObjectByName(word)) r->doSomething; 有什么原因不起作用?
【解决方案3】:

在 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

【讨论】:

    【解决方案4】:

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

    • 不要再试图将这一切集中在一条线上。例如:

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

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

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

    我想我只会使用else 表单,如果真的 将所有内容放在一行中很重要(例如,保留代码的表格格式)。

    【讨论】:

    • r 可能不是智能指针,例如一个Status 类,它转换为false 以指示失败并且可以例如在这种情况下提供错误消息。
    • @Ruslan 是的,它可以,但是重载operator -&gt; 以提供错误消息对我来说似乎是运算符重载滥用。 r.Error().doSomething() 会更干净。
    • 嗯,确实,没想到operator-&gt;
    【解决方案5】:

    您正在尝试做的事情很好。通过在 if 中定义变量,您可以限制它的 范围。这有助于在它们发挥作用后减少杂散变量的数量。

    使用这种技术,如果你想遵循否定路径,你需要像这样使用else

    if(auto r = getGlobalObjectByName(word))
    {
        r->doSomething();
    }
    else
    {
        // r == nullptr
        // so do something else
    }
    

    【讨论】:

      最近更新 更多