Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deduce return type of member function

In a template function I was trying to create a std::vector with its value_type being dependent on a member function of the template parameter to the function. This template parameter is restricted to being a vector containing unique pointers of a certain type with a certain function. For example:

/* somewhere in the code */
std::vector< std::unique_ptr< Widget > > myVec;
/* work with myVec and fill it, then call the relevant function */
func(myVec);

Now the function func needs to retrieve the return type of the member function member_func of Widget. Note that Widget can also be a different type, as long as it has the member function member_func.

template <typename Vec>
void func(const Vec& vec) {
  using ret_type = decltype(Vec::value_type::element_type::member_func()); // Doesn't work
  std::vector< ret_type > local_vec;
}

I've tried various things, e.g. std::result_of, std::invoke_result and decltype, but I can't seem to make it work. Is this even possible and if yes, how could it be achieved?

like image 986
KorbenDose Avatar asked Jun 06 '18 09:06

KorbenDose


1 Answers

Is this close to what you wanted?

#include <vector>
#include <utility>
#include <memory>

struct Foo
{
    int member_func();
};

template <typename Vec>
void func(const Vec& vec) {

    using ret_type = decltype(std::declval<typename Vec::value_type>()->member_func());

    std::vector< ret_type > local_vec;
}


int main()
{
    std::vector<std::unique_ptr<Foo>> v;
    func(v);
}

Demo: https://godbolt.org/g/dJkSf1

Explanation:

std::declval<typename Vec::value_type>() generates a reference to a unique_ptr (which must be used in an unevaluated context). We then take the decltype of calling generated_reference->member_function().

This would be the same type as the result of vec[0]->member_func()

Indeed, we could write it this way:

template <typename Vec>
void func(const Vec& vec) {

    using ret_type = decltype(vec.at(0)->member_func());

    std::vector< ret_type > local_vec;
}

Which might be more expressive and generic (Vec may now be any type which is vector-like and holds pointer-like things to Foo)

Furthermore, the more generically we approach the deduction, the more generic our func function becomes:

#include <vector>
#include <utility>
#include <memory>
#include <set>
#include <iterator>

struct Foo
{
    int member_func();
};

template <typename Vec>
void func(const Vec& vec) {

    using ret_type = decltype((*std::begin(vec))->member_func());

    std::vector< ret_type > local_vec;
}


int main()
{
    std::vector<std::unique_ptr<Foo>> v;
    func(v);
    func(std::array<std::unique_ptr<Foo>, 10> { });

    Foo* foos[] = { nullptr, nullptr };
    func(foos);

    func(std::set<std::shared_ptr<Foo>, std::owner_less<>> {});
}

Note

This code assumes that the return_type of Foo::member_func is not a reference type.

If that's a possibility, we'd need to decide whether we used metaprogramming to:

a) convert reference types to std::reference_wrapper, so they can be stored in a vector, or

b) convert reference types to fundamental types using std::decay, which would result in copies being made.

like image 86
Richard Hodges Avatar answered Oct 08 '22 22:10

Richard Hodges