是否可以声明 C++ 函数使得 return 值不能被忽略?

Can a C++ function be declared such that the return value cannot be ignored?

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

这是一个示例程序:

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++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();
}

概要:函数 return 不是 return 一个 T,而是一个 angry<T> 函数,如果未提取值,它将通过抛出 logic_error 来惩罚调用者在销毁之前。

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

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

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

参见__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"

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

  1. 获取C++17即可使用[[nodiscard]]
  2. 在 g++(还有 clang++)中,使用像 __wur 这样的编译器扩展(定义 作为 __attribute__ ((__warn_unused_result__))),或更便携的(仅限 C++11 及更高版本)[[gnu::warn_unused_result]] 属性。
  3. 在单元测试期间使用运行时检查来捕获问题

如果这3个都做不到,那么还有一种方法,就是"Negative compiling"。定义你的 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() 并检查它的 return 相比,这可能要好一些,因为可能有许多像 foo() 这样的函数,您可能不想忽略 return值。

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

如果您使用 MFC ,您可以在函数声明之前尝试 Check_return 。 在 Annotating function behavior

上查看更多相关信息