[英][[nodiscard]] in std::function return type definition?
我想知道是否有办法拥有这样的东西:
using CallbackType = std::function<[[nodiscard]]bool(void)>;
(我知道上面的代码不会被编译并抱怨nodiscard
不能应用于类型!)
我的目标是强制回调的调用者检查它的返回值!
你可以做这样的事情
#include <iostream>
#include <utility>
template <typename R, typename... Args>
struct Function {
using Fn = R (*)(Args...);
Fn fn;
explicit Function(Fn fn) : fn{fn} {}
[[nodiscard]] R operator()(Args... args) {
return (*fn)(std::forward<Args>(args)...);
}
};
template <typename R, typename... Args>
Function(R (*)(Args...)) -> Function<R, Args...>;
bool bar(const int& x) { return x % 2 == 0; }
int main() {
Function{&bar}(10);
}
警告:
Compiler stderr
<source>: In function 'int main()':
<source>:24:17: warning: ignoring return value of 'R Function<R, Args>::operator()(Args ...) [with R = bool; Args = {const int&}]', declared with attribute 'nodiscard' [-Wunused-result]
24 | Function{&bar}(10);
| ~~~~~~~~~~~~~~^~~~
<source>:12:19: note: declared here
12 | [[nodiscard]] R operator()(Args... args) {
| ^~~~~~~~
编辑:扩展到成员函数 (+const) + lambda(带推导指南)
template <typename T, typename = std::void_t<>>
struct Function;
template <typename R, typename... Args>
struct Function<R (*)(Args...)> {
using Fn = R (*)(Args...);
Fn fn;
explicit Function(Fn fn) : fn{fn} {}
[[nodiscard]] R operator()(Args... args) {
return fn(std::forward<Args>(args)...);
}
};
template <typename T, typename R, typename... Args>
struct Function<R (T::*)(Args...)> {
using MFn = R (T::*)(Args...);
T* t;
MFn mfn;
Function(T* t, MFn mfn) : t{t}, mfn{mfn} {}
[[nodiscard]] R operator()(Args... args) {
return (t->*mfn)(std::forward<Args>(args)...);
}
};
template <typename T, typename R, typename... Args>
struct Function<R (T::*)(Args...) const> {
using MFn = R (T::*)(Args...) const;
const T* t;
MFn mfn;
Function(const T* t, MFn mfn) : t{t}, mfn{mfn} {}
[[nodiscard]] R operator()(Args... args) {
return (t->*mfn)(std::forward<Args>(args)...);
}
};
template <typename T>
struct Function<T, std::void_t<decltype(&T::operator())>> final
: Function<decltype(&T::operator())> {
explicit Function(const T& t)
: Function<decltype(&T::operator())>(&t, &T::operator()) {}
};
template<typename T>
Function(T) -> Function<T>;
template<typename T, typename R, typename ...Args>
Function(T*, R(T::*)(Args...)) -> Function<R(T::*)(Args...)>;
template<typename T, typename R, typename ...Args>
Function(const T*, R(T::*)(Args...) const) -> Function<R(T::*)(Args...) const>;
void foo() {}
struct Foo
{
void foo() const {}
};
int main() {
Function{&foo}();
Function{[]{}}();
Foo foo{};
Function{&foo, &Foo::foo}();
}
您可以直接包装std::function
或真正的 lambda 以修改诸如[[nodiscard]]
类的属性。
您关于公共继承有问题的评论是绝对正确的。 但是,这不适用于私有继承,因为您可以控制这些问题。
编辑:但是如果你绝对不喜欢它,你可以重写下面的代码以使用私有成员而不是私有继承,代价是使代码更冗长,但有支持原始函数指针的好处。
template <typename Function>
struct NoDiscard : private Function {
NoDiscard() = default;
NoDiscard(NoDiscard const&) = default;
NoDiscard(NoDiscard&&) = default;
NoDiscard& operator=(NoDiscard const&) = default;
NoDiscard& operator=(NoDiscard&&) = default;
NoDiscard(Function&& fn) : Function(std::move(fn)) {}
NoDiscard(Function const& fn) : Function(fn) {}
template <typename... Ts>
[[nodiscard]] auto operator()(Ts&&... ts) {
return Function::operator()(std::forward<Ts>(ts)...);
}
};
// convenience function to make the call look intuitive
template <typename Function>
auto add_nodiscard(Function&& f) {
return NoDiscard<Function>(std::forward<Function>(f));
}
这将允许您简单地将add_nodiscard
到任何函子或可调用类型。 例子:
void test_good(auto fn) {
(void)fn();
}
void test_bad(auto fn) {
fn();
}
int main() {
test_good( ( ([](){return true;})));
test_bad ( ( ([](){return true;})));
test_good(add_nodiscard( ([](){return true;})));
test_bad (add_nodiscard( ([](){return true;}))); // warns about discarded result
test_good( (std::function<bool(void)>([](){return true;})));
test_bad ( (std::function<bool(void)>([](){return true;})));
test_good(add_nodiscard(std::function<bool(void)>([](){return true;})));
test_bad (add_nodiscard(std::function<bool(void)>([](){return true;}))); // warns about discarded result
}
这有一个警告,您不能将 this 与必须接收std::function
对象的类型一起使用。
我相信这样的目标是不可能的,因为您无法更改库函数的类型或直接向其添加属性。 并且您在std::function
调用之前添加的任何包装器都不会对[[nodiscard]]
产生影响,因为std::function
将尽职尽责地从其被调用方返回返回值。
免责声明:我是说这绝对不可能,其唯一目的是被证明是错误的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.