简体   繁体   中英

Matching Pointer to members and its type as template parametes

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

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
    } 
};

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