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?
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<>> {});
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With