简体   繁体   中英

Deduce template-argument from std::function's return type

I usually never write C++ and today I tried experimented with C++ templates. I implemented a Maybe type which looks like this

#include <functional>
#include <iostream>
#include <string>
template<typename T>
class TMaybe
{
  T value;
public:
  TMaybe() : value(nullptr){}
  TMaybe(T &&v) : value(v){}
  TMaybe(T v) : value(v){}
};

template<typename T, typename R>
TMaybe<R> maybe_if(const TMaybe<T> &m, std::function<R(T v)> f){
  return (m.value != nullptr) ? TMaybe<R>(f(m)) : TMaybe();
}

int main(){
  int i = 10;
  auto m = TMaybe<int>(i);
  auto plus_ten = [](int i) -> int {return i + 10;};
  maybe_if(m, plus_ten); // could not deduce template argument for 'std::function<R(T)>' from 'main::<lambda_17413d9c06b6239cbc7c7dd22adf29dd>'
}

but the error message could not deduce template argument for 'std::function<R(T)>' from 'main::<lambda_17413d9c06b6239cbc7c7dd22adf29dd>' is not very helpful. Can you spot the error?

The compiler can only deduce R from f if you pass it an actual instance of std::function<R(T)> ; passing a lambda won't work, as a lambda isn't an instance of a std::function specialization.

The correct way to write your code is to allow any functor type, and deduce R from it:

template<typename T, typename F, typename R = typename std::result_of<F(T)>::type>
TMaybe<R> maybe_if(const TMaybe<T> &m, F f){
  return (m.value != nullptr) ? TMaybe<R>(f(m.value)) : TMaybe();
}

Template argument deduction happens before implicit conversion of Lambda to std::fucntion .

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution , which happens later.

Hence compiler can only deduce " R " (return-type) from " f " (functor) if you pass an actual instance of std::function<R(T)> type.

Example for C++20 (and maybe later)

Use std::type_identity_t , like:

#include <type_traits>

// ...

template<typename T, typename R>
TMaybe<R> maybe_if(const TMaybe<T> &m, std::function< std::type_identity_t<R> (T v)> f)
{
  return (m.value != nullptr) ? TMaybe<R>(f(m)) : TMaybe();
}

Example for older compilers

If C++20 is not allowed for your project, you need to take the functor-type as template-argument, and deduce R from that.

For MSVC 2015 use std::result_of

template<typename T, typename F, typename R = typename std::result_of<F(T)>::type>
TMaybe<R> maybe_if(const TMaybe<T> &m, F f)
{
  return (m.value != nullptr) ? TMaybe<R>(f(m.value)) : TMaybe();
}

For MSVC 2010 use decltype

MSVC 2010 has std::result_of , but does not allow default template argument for functions, hence we do something like:

#define MY_RESULT_OF(functor, argType) decltype( functor(*static_cast<argType * >(nullptr)) )

template<typename T, typename F>
auto maybe_if(const TMaybe<T> &m, F f) -> TMaybe< MY_RESULT_OF(f, T) >
{
  typedef MY_RESULT_OF(f, T) R;
  return (m.value != nullptr) ? TMaybe<R>(f(m.value)) : TMaybe();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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