Reputation: 47
Given this code
struct data {
int velocity;
};
template <typename Data>
class Collector {
// ...
public:
void add(const Data& data) {}
template <typename T>
T average1(T Data::*field) const {
return T{}; // Some calculation here
}
template <T Data::*field>
T average2() const {
return T{}; // Some calculation here
}
};
void foo() {
Collector<data> collector;
// I have no problem handling the average by sending member as parameter
auto ok = collector.average1(&data::velocity);
// But compilation here fails
auto error = collector.average2<&data::velocity>();
}
My intention is to replace passing pointers to members to functions by template arguments, but could not match simultaneously member type and member, I can do something like
template <typename T, T Data::*field>
T average2() const {
return T{}; // Some calculation here
}
but then I have to invoke as
auto error = collector.average2<int, &data::velocity>();
and that is a but ugly and seems unnecessary
Do you have an idea on how to fix this or a better approach to collect this kind of data?
Thanks in advance
Upvotes: 1
Views: 117
Reputation:
In C++17, you can make the templated version work by broadening the value template parameter to auto
and resolve T
later down the line, with a decltype()
for example.
#include <type_traits>
template <typename Data>
class Collector {
// ...
public:
template <auto field>
auto average2() const {
using T = decltype(std::declval<Data>().*field);
return T{}; // Some calculation here
}
};
void foo() {
Collector<data> collector;
// Works perfectly fine
auto error = collector.average2<&data::velocity>();
}
In C++20, you can make this cleaner still by constraining field
to Data
member pointers. This will give you tighter overload resolution as well as nicer error messages.
#include <type_traits>
template<typename PtrT, typename ObjT>
concept MemberObjectPointerFor = std::is_member_object_pointer_v<PtrT> &&
requires(PtrT ptr, ObjT& obj) {
{ obj.*ptr };
};
template <typename Data>
class Collector {
// ...
public:
template <MemberObjectPointerFor<Data> auto field>
auto average2() const {
using T = decltype(std::declval<Data>().*field);
return T{}; // Some calculation here
}
};
Upvotes: 4