【问题标题】:Can a C++ function be declared such that the return value cannot be ignored?可以声明 C++ 函数以使返回值不能被忽略吗?
【发布时间】:2016-09-05 08:58:05
【问题描述】:

我正在尝试确定是否可以以不能忽略返回值的方式声明 C++ 函数(最好在编译时检测到)。我尝试使用 private(或在 C++11 中,deleted)operator void() 声明一个类,以尝试在未使用返回值时捕获到 void 的隐式转换。

这是一个示例程序:

class Unignorable {
    operator void();
};

Unignorable foo()
{
    return Unignorable();
}

int main()
{
    foo();
    return 0;
}

不幸的是,我的编译器 (clang-703.0.31) 说:

test.cpp:2:5: warning: conversion function converting 'Unignorable' to 'void' will never be used
    operator void();
    ^

并且不会在调用foo() 时引发任何错误或警告。所以,那是行不通的。有没有其他方法可以做到这一点?特定于 C++11 或 C++14 或更高版本的答案会很好。

【问题讨论】:

标签: c++ return-value void


【解决方案1】:

从其他答案和cmets总结,基本上你有3个选择:

  1. 获取 C++17 可以使用[[nodiscard]]
  2. 在 g++(也是 clang++)中,使用编译器扩展,例如 __wur(定义 __attribute__ ((__warn_unused_result__))),或者更便携的(仅限 C++11 及更高版本)[[gnu::warn_unused_result]] 属性。
  3. 在单元测试期间使用运行时检查来发现问题

如果这三个都不可能,那么还有另一种方法,那就是“负编译”。如下定义您的Unignorable

struct Unignorable {
  Unignorable () = default;
#ifdef NEGATIVE_COMPILE
  Unignorable (const Unignorable&) = delete;  // C++11
  Unignorable& operator= (const Unignorable&) = delete;
  //private: Unignorable (const Unignorable&); public:  // C++03
  //private: Unignorable& operator= (const Unignorable&); public: // C++03
  /* similar thing for move-constructor if needed */
#endif
};

现在使用 -DNEGATIVE_COMPILE 或其他编译器(如 MSVC)中的等效项进行编译。它会在结果未被忽略的地方给出错误

auto x = foo();  // error

但是,无论结果被忽略,它都不会给出任何错误:

foo(); // no error

使用任何现代代码浏览器(如 eclipse-cdt),您可能会发现所有出现的 foo() 并修复那些没有出错的地方。在新编译中,只需删除“NEGATIVE_COMPILE”的预定义宏。

与简单地查找 foo() 并检查其返回相比,这可能要好一些,因为可能有许多函数,如 foo(),您可能不想忽略返回值。

这有点乏味,但适用于具有所有编译器的所有 C++ 版本。

【讨论】:

    【解决方案2】:

    在 c++17 之前,我想到了这种方法:

    #include <stdexcept>
    #include <exception>
    #include <boost/optional.hpp>
    
    // proxy object which complains if it still owns the return
    // value when destroyed
    template<class T>
    struct angry
    {
      angry(T t) : value_(std::move(t)) {} 
      angry(angry&&) = default;
      angry(angry const&) = default;
      angry& operator=(angry&&) = default;
      angry& operator=(angry const&) = default;
    
      ~angry() noexcept(false)
      {
        if (value_) throw std::logic_error("not used");
      } 
    
      T get() && { 
        T result = std::move(value_).value();
        value_.reset();
        return result; 
      }
    
      boost::optional<T> value_;
    };
    
    // a function which generates an angry int    
    angry<int> foo()
    {
      return 10;
    }
    
    int main()
    {
      // obtain an int
      auto a = foo().get();
    
      // this will throw
      foo();
    }
    

    概要:一个函数不是返回一个 T,而是返回一个 angry&lt;T&gt;,如果在销毁之前没有提取该值,它将通过抛出一个 logic_error 来惩罚调用者。

    这是一个运行时解决方案,这是一个限制,但至少应该在单元测试的早期发现。

    精明的用户当然可以颠覆它:

    foo().get();  // won't throw
    

    【讨论】:

    • 这是一个有趣的方法,我赞成它,但理想情况下我正在寻找一个编译时解决方案。我不关心调用者是否有能力颠覆它,因为他们总是可以通过将结果分配给某物然后干脆不使用该值。
    • @GregHewgill 那么我认为你在 c++17 之前都不走运,除非编译器扩展
    【解决方案3】:

    __attribute__ ((warn_unused_result))

    int foo() __attribute__ ((warn_unused_result));
    int foo(){return 123;}
    
    int main()
    {
        foo(); //compiler warning
        auto i = foo(); //valid
    }
    

    然后强制警告为错误:

    clang++ -std=c++1z -Werror="unused-result"
    

    【讨论】:

    • 在 G++ 中(可能也是 clang++),我们也可以使用__wur 作为__attribute__ ((__warn_unused_result__)) 的简写宏。这个宏在::system(...)函数之后使用。
    【解决方案4】:

    如果您使用 MFC ,您可以在函数声明之前尝试 Check_return。 在Annotating function behavior 上查看更多信息

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-21
      • 2021-04-16
      • 2016-03-26
      • 2017-06-16
      • 1970-01-01
      相关资源
      最近更新 更多