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

时间:2016-09-05 08:58:06

标签: c++ return-value void

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

这是一个示例程序:

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或更高版本的答案可以。

4 个答案:

答案 0 :(得分:8)

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

  1. 获取C ++ 17以便能够使用[[nodiscard]]
  2. 在g ++(也是clang ++)中,使用像__wur这样的编译器扩展(已定义 作为__attribute__ ((__warn_unused_result__))),或更可移植(仅限C ++ 11及以上)[[gnu::warn_unused_result]]属性。
  3. 使用运行时检查来捕获单元测试期间的问题
  4. 如果所有这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 ++版本。

答案 1 :(得分:6)

__attribute__ ((warn_unused_result))

Failed to start service jboss.persistenceunit."app.ear#PU": org.jboss.msc.service.StartException in service jboss.persistenceunit."PU": javax.persistence.PersistenceException: [PersistenceUnit: PU] Unable to build Hibernate SessionFactory
      ...
      Caused by: javax.persistence.PersistenceException: [PersistenceUnit: PU] Unable to build Hibernate SessionFactory
      ...
      Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Unable to execute schema management to JDBC target [create index company_id_index on APPROVER (COMPANY_ID)]
      ...
      Caused by: org.postgresql.util.PSQLException: ERROR: relation "company_id_index" already exists

然后强制警告为错误:

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

int main()
{
    foo(); //compiler warning
    auto i = foo(); //valid
}

答案 2 :(得分: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();
}

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

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

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

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

答案 3 :(得分:1)

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

上了解更多