【问题标题】:Matching Pointer to members and its type as template parametes匹配指向成员的指针及其作为模板参数的类型
【发布时间】:2021-10-29 13:35:07
【问题描述】:

鉴于此代码

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

我的意图是用模板参数替换指向函数的成员指针,但不能同时匹配成员类型和成员,我可以做类似的事情

template <typename T, T Data::*field>
T average2() const {
    return T{}; // Some calculation here
}

但是我必须调用 as

auto error = collector.average2<int, &data::velocity>();

那是丑陋的,似乎没有必要

您对如何解决此问题或收集此类数据有更好的方法有什么想法吗?

提前致谢

【问题讨论】:

    标签: c++ templates template-matching pointer-to-member


    【解决方案1】:

    在 C++17 中,您可以通过将值模板参数扩展为 auto 并稍后解析 T 来使模板化版本工作,例如 decltype()

    #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>();
    }
    

    在 C++20 中,您可以通过将 field 限制为 Data 成员指针来使其更清晰。这将为您提供更严格的重载解决方案以及更好的错误消息。

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

    【讨论】:

    • 话虽如此,我个人还是坚持average1()。这是编译器在大多数情况下可以轻松优化的东西。
    • 我认为在阅读代码时使用模板参数而不是占位符函数 arg 更有意义和直观。然而,它非常依赖于 c++20,因此使用这种方法很有价值。 OP 没有标记 c++ 版本,所以它是合法的,但目前仍然有限制。到那个地步。这是非常前沿的。虽然这在 clang (trunk) 中运行良好,但 gcc (trunk) 目前在这种方法上遇到了一个内部错误:godbolt.org/z/jn8n3Y9qr [发布日期:2021/10/29] 已知问题 - 还是我应该归档?
    • @GlennTeitelbaum 这个概念对于这个工作来说并不是绝对必要的,它只会让事情变得更干净一些。基本的auto 版本是 C++17,它不再是真正的前沿。 gcc 错误当然是不幸的
    猜你喜欢
    • 2019-01-04
    • 2013-02-15
    • 1970-01-01
    • 2015-01-16
    • 1970-01-01
    • 2017-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多