簡體   English   中英

[[nodiscard]] 在 std::function 返回類型定義中?

[英][[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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM