简体   繁体   English

[[nodiscard]] 在 std::function 返回类型定义中?

[英][[nodiscard]] in std::function return type definition?

I'm wondering if is there a way to have something like this:我想知道是否有办法拥有这样的东西:

using CallbackType = std::function<[[nodiscard]]bool(void)>; 

(I know above code won't be compiled and complains that nodiscard can't be applied to types!) (我知道上面的代码不会被编译并抱怨nodiscard不能应用于类型!)

My goal is to enforce the caller of the callback to check the return value of it!我的目标是强制回调的调用者检查它的返回值!

you can do something like this你可以做这样的事情

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

Warning:警告:

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) {

      |                   ^~~~~~~~

EDIT: Extend to member function (+const) + lambda ( with deduction guide )编辑:扩展到成员函数 (+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}();
}

You can wrap std::function or really a lambda directly to amend an attribute such as [[nodiscard]] .您可以直接包装std::function或真正的 lambda 以修改诸如[[nodiscard]]类的属性。

Your comment about public inheritance being problematic is absolutely correct.您关于公共继承有问题的评论是绝对正确的。 However, this does not apply to private inheritance as you can control for these problems.但是,这不适用于私有继承,因为您可以控制这些问题。
Edit: But if you absolutely don't like it, you can rewrite the code below to use a private member instead of private inheritance at the cost of making the code a bit more verbose but the benefit of supporting raw function pointers.编辑:但是如果你绝对不喜欢它,你可以重写下面的代码以使用私有成员而不是私有继承,代价是使代码更冗长,但有支持原始函数指针的好处。

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

This will allow you to simply add_nodiscard to any functor or callable type.这将允许您简单地将add_nodiscard到任何函子或可调用类型。 Example:例子:

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
}

live demo现场演示

This has the caveat that you cannot use this with a type that must receive a std::function object.这有一个警告,您不能将 this 与必须接收std::function对象的类型一起使用。

I believe such a goal to be impossible because you cannot change the type of a library function or add attributes directly to it.我相信这样的目标是不可能的,因为您无法更改库函数的类型或直接向其添加属性。 And any wrapper you add before the std::function call will have no effect regarding [[nodiscard]] simply because std::function will dutifully return the returned value from its callee.并且您std::function调用之前添加的任何包装器都不会对[[nodiscard]]产生影响,因为std::function将尽职尽责地从其被调用方返回返回值。

Disclaimer: I am saying that this absolutely impossible with the sole purpose of being proven wrong.免责声明:我是说这绝对不可能,其唯一目的是被证明是错误的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM