How to convert std :: result_of to decltype in template argument

In CppCon 2015, S. Lavavej of Microsoft said to avoid use result_of. But I have a situation where I cannot find a suitable alternative.

Consider the following code. Is there a way to change std::result_of<F()>::typeinstead decltype?

#include <iostream>
#include <future>
#include <type_traits>

template<typename T>
struct NopErrCB
{
  constexpr T operator() () const { return T(); }
};

template <typename Func, typename ErrCB>
struct SafeTaskWrapper {
  Func f;
  ErrCB errCB;

  template <typename... T>
  auto operator()(T&&... args) -> decltype(f(args...)) {
    try
    {
      return f(std::forward<T>(args)...);
    }
    catch (...)
    {
      return errCB();
    }
  }

};
//                                   vvv OVER HERE  vvv
template <typename F>
SafeTaskWrapper<F, NopErrCB<typename std::result_of<F()>::type>> make_safe_task(F&& f) {
  return { std::forward<F>(f) };
}

template <typename F, typename ErrCB>
SafeTaskWrapper<F, ErrCB> make_safe_task(F&& f, ErrCB&& cb) {
  return { std::forward<F>(f), std::forward<ErrCB>(cb) };
}

int main() {
  auto futA = std::async(std::launch::async, make_safe_task([] { throw "A"; return 1; }));
  auto futB = std::async(std::launch::async, make_safe_task([] { throw "B"; return 1; }, [] { return 2; }));
  auto futC = std::async(std::launch::async, make_safe_task([] { throw "C"; }));
  std::cout << futA.get() << std::endl;
  std::cout << futB.get() << std::endl;
  futC.get();
}

<sub> PS. Please do not mind the purpose of SafeTaskWrapper. I know that the future is already handling C ++ exceptions. This is just a demonstration, the actual code is designed to handle Windows SEH exceptions, but it does not matter for this question.

+4
source share
4 answers

you can use

decltype(std::declval<F>()())

instead

typename std::result_of<F()>::type

,

+4

, result_of . , result_of, . decltype.

-, , result_of .

struct evil {
  template<class...Args>
  int operator()(Args&&)&&{return 0;}
  template<class...Args>
  std::string operator()(Args&&)const&{return "hello";}
};

evil{} , . , f SafeTaskWrapper lvalue, result_of rvalue.

, S.T.L. .

std::result_of_t<F(Args...)> 1 decltype( std::declval<F>()( std::declval<Args>()... ) ), decltype( std::declval<F>()()). result_of_t decltype .

, .

struct anything_really {
  template<class T> operator T() const { return {}; }
};
struct return_anything {
  anything_really operator()() const { return {}; }
};

errCB. , .

, " ". :

template<class R>
struct smart_invoke {
  template<class F>
  R operator()(F&& f)const {
    return std::forward<F>(f)();
  }
};
template<>
struct smart_invoke<void> {
  template<class F>
  void operator()(F&& f)const {
    std::forward<F>(f)();
  }
};

:

template <typename Func, typename ErrCB=return_anything>
struct SafeTaskWrapper {
  Func f;
  ErrCB errCB;

  template <class... Ts>
  decltype(auto) operator()(Ts&&... args) {
    try {
      return f(std::forward<T>(args)...);
    } catch (...) {
      using R=decltype( f( std::forward<T>(args)... ) );
      return smart_invoke<R>{}(errCB);
    }
  }
};

factory. , .

template <class ErrCB=return_anything, class F>
SafeTaskWrapper<F, ErrCB> make_safe_task(F&& f, ErrCB&& cb = {}) {
  return { std::forward<F>(f), std::forward<ErrCB>(cb) };
}

, errCB , .

result_of_t .

trailing return decltype . , (key), .


1 , f - .., . ++ 17 std::invoke operator() decltype. , , .

+4

What is used std::result_of. I think that's enough.

If you need shorter and more readable code, consider the use case std::result_of_tthat is added after C ++ 14.

//NopErrCB<typename std::result_of<F()>::type>
NopErrCB<std::result_of_t<F()>> // equal
+2
source

Direct translation will be:

template <typename F>
SafeTaskWrapper<F, NopErrCB<decltype(std::declval<F>()())>> make_safe_task(F&& f) {
  return { std::forward<F>(f) };
}

Which seems to me more clear.

+1
source

Source: https://habr.com/ru/post/1655598/


All Articles