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

蓝咒 提交于 2019-11-30 13:10:12

问题


I'm trying to determine whether a C++ function can be declared in such a way that the return value cannot be ignored (ideally detected at compile time). I tried to declare a class with a private (or in C++11, deleted) operator void() to try to catch the implicit conversion to void when a return value is unused.

Here's an example program:

class Unignorable {
    operator void();
};

Unignorable foo()
{
    return Unignorable();
}

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

Unfortunately, my compiler (clang-703.0.31) says:

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

and doesn't raise any error or warning on the call to foo(). So, that won't work. Is there any other way to do this? Answers specific to C++11 or C++14 or later would be fine.


回答1:


To summarize from other answers & comments, basically you have 3 choices:

  1. Get C++17 to be able to use [[nodiscard]]
  2. In g++ (also clang++), use compiler extensions like __wur (defined as __attribute__ ((__warn_unused_result__))), or the more portable (C++11 and up only) [[gnu::warn_unused_result]] attribute.
  3. Use runtime checks to catch the problem during unit testing

If all of these 3 are not possible, then there is one more way, which is kind of "Negative compiling". Define your Unignorable as below:

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
};

Now compile with -DNEGATIVE_COMPILE or equivalent in other compilers like MSVC. It will give errors at wherever the result is Not ignored:

auto x = foo();  // error

However, it will not give any error wherever the result is ignored:

foo(); // no error

Using any modern code browser (like eclipse-cdt), you may find all the occurrences of foo() and fix those places which didn't give error. In the new compilation, simply remove the pre-defined macro for "NEGATIVE_COMPILE".

This might be bit better compared to simply finding foo() and checking for its return, because there might be many functions like foo() where you may not want to ignore the return value.

This is bit tedious, but will work for all the versions of C++ with all the compilers.




回答2:


See __attribute__ ((warn_unused_result)).

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

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

Then force the warning to be an error:

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



回答3:


Prior to c++17 this approach came to mind:

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

Synopsis: rather than return a T, a function returns an angry<T> which will punish the caller by throwing a logic_error if the value is not extracted prior to destruction.

It's a run-time solution, which is a limitation, but at least ought to be caught early in unit tests.

A canny user can of course subvert it:

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


来源:https://stackoverflow.com/questions/39327028/can-a-c-function-be-declared-such-that-the-return-value-cannot-be-ignored

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!