是否可以声明 C++ 函数使得 return 值不能被忽略?
Can a C++ function be declared such that the return value cannot be ignored?
我正在尝试确定是否可以以 return 值不能被忽略的方式声明 C++ 函数(最好在编译时检测到)。我尝试用 private
(或在 C++11 中,delete
d)声明 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 个选择:
- 获取C++17即可使用
[[nodiscard]]
- 在 g++(还有 clang++)中,使用像
__wur
这样的编译器扩展(定义
作为 __attribute__ ((__warn_unused_result__))
),或更便携的(仅限 C++11 及更高版本)[[gnu::warn_unused_result]]
属性。
- 在单元测试期间使用运行时检查来捕获问题
如果这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
上查看更多相关信息
我正在尝试确定是否可以以 return 值不能被忽略的方式声明 C++ 函数(最好在编译时检测到)。我尝试用 private
(或在 C++11 中,delete
d)声明 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 个选择:
- 获取C++17即可使用
[[nodiscard]]
- 在 g++(还有 clang++)中,使用像
__wur
这样的编译器扩展(定义 作为__attribute__ ((__warn_unused_result__))
),或更便携的(仅限 C++11 及更高版本)[[gnu::warn_unused_result]]
属性。 - 在单元测试期间使用运行时检查来捕获问题
如果这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
上查看更多相关信息