是否可以声明C++函数,以便不能忽略返回值?

Gre*_*ill 24 c++ return-value void

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

这是一个示例程序:

class Unignorable {
    operator void();
};

Unignorable foo()
{
    return Unignorable();
}

int main()
{
    foo();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

iam*_*ind 9

总结其他答案和评论,基本上你有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
};
Run Code Online (Sandbox Code Playgroud)

现在-DNEGATIVE_COMPILE在MSVC等其他编译器中编译或等效.它会在结果不被忽略的地方给出错误:

auto x = foo();  // error
Run Code Online (Sandbox Code Playgroud)

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

foo(); // no error
Run Code Online (Sandbox Code Playgroud)

使用任何现代代码浏览器(如eclipse-cdt),您可以找到所有出现foo()并修复那些没有给出错误的地方.在新编译中,只需删除"NEGATIVE_COMPILE"的预定义宏.

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

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


Tre*_*key 6

请参阅__attribute __((warn_unused_result)).

int foo() __attribute__ ((warn_unused_result));
int foo(){return 123;}

int main()
{
    foo(); //compiler warning
    auto i = foo(); //valid
}
Run Code Online (Sandbox Code Playgroud)

然后强制警告是一个错误:

clang++ -std=c++1z -Werror="unused-result"
Run Code Online (Sandbox Code Playgroud)


Ric*_*ges 5

在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();
}
Run Code Online (Sandbox Code Playgroud)

概要:一个函数返回一个函数,而不是返回一个T,如果在销毁之前未提取该值,则angry<T>抛出一个将惩罚调用者logic_error.

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

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

foo().get();  // won't throw
Run Code Online (Sandbox Code Playgroud)