【问题标题】:Converting a pointer-to-member type to a simple pointer type将指向成员的指针类型转换为简单的指针类型
【发布时间】:2021-09-24 10:34:58
【问题描述】:

我有以下类型,我用decltype得到的

QString UserInfo::*&

我可以通过用std::remove_reference_t 包裹decltype 来删除& 部分,但我也想删除UserInfo::* 部分

我该怎么做才能在我的模板中只使用 QString 类型

我在初始化列表中使用此模板,我无法访问实体对象或decltype 中指向.* 运算符的指针

【问题讨论】:

  • std::declval 允许您拥有 T 类型的对象,因此 decltype(std::declval<UserInfo>().someMember) 应该可以解决问题
  • @MarekR:这是对类型而非值的元编程操作。没有表情。
  • 我希望我能接受两个答案

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


【解决方案1】:

在未评估的上下文(如decltype)中不需要使用有效对象。稍微夸张一点,您甚至可以在其中取消引用一个空指针,并且不会发生任何不好的事情,因为取消引用实际上从未被计算过。

要创建类型无效但可用于未评估上下文的对象,您可以使用std::declval

template<class T>
using member_type = decltype(std::declval<UserInfo>().*std::declval<T>());

【讨论】:

  • IMO 这是一个糟糕的解决方案,因为它与UserInfo 永久关联,因此它仅适用于此类。其他答案没有这个问题。
【解决方案2】:

使用模板特化提取成员类型:

template <typename MemberType>
struct member_type;

template <typename Class, typename Member>
struct member_type<Member Class::*>
{
    using type = Member;
};

template<typename T>
using member_type_t = typename member_type<T>::type;


class A
{
public:
    int b;
};

using t = member_type_t<decltype(&A::b)>;

【讨论】:

    【解决方案3】:

    你可以写一个trait来从成员函数指针中提取成员的类型:

    #include <type_traits>
    
    template <typename T>
    struct member_type;
    
    template <typename C, typename T>
    struct member_type<T C::*> {
        using type = T;
    };
    
    template <typename T>
    using member_type_t = typename member_type<T>::type;
    
    struct foo {
        int bar;
    };
    
    
    int main()
    {
        int foo::*ptr = &foo::bar;
        using T = member_type_t<decltype(ptr)>;
        static_assert( std::is_same_v<int,T>);
    }
    

    【讨论】:

      【解决方案4】:

      这是我使用 C++17 的版本,使用模板时不需要decltype(更方便一点):

      #include <iostream>
      #include <string>
      #include <type_traits>
      
      template <auto Fp>
      struct field_type;
      
      template<typename R, typename T, R (T::*FP)>
      struct field_type<FP>
      {
          using type = R;
      };
      
      template<typename R, typename T, typename...Args, R (T::*FP)(Args...)>
      struct field_type<FP>
      {
          using type = R;
      }; 
      
      template<auto FP>
      using field_type_t = typename field_type<FP>::type;
      
      
      class Foo {
      public:
          int x = 0;
          double y = 0;
          std::string s;
          const int cx = 0;
      
          Foo() = default;
      
          void bar() {
              std::cout << "bar\n";
          }
      
          int par(int z) {
              std::cout << "bar\n";
              return z;
          }
      };
      
      template<auto F, typename T>
      constexpr bool test = std::is_same_v<field_type_t<F>, T>;
      
      static_assert(test<&Foo::x,    int>,         "");
      static_assert(test<&Foo::cx,   const int>,   "");
      static_assert(test<&Foo::s,    std::string>, "");
      static_assert(test<&Foo::y,    double>,      "");
      #ifndef HIDE_PROBLEM_ON_GCC_11
      static_assert(test<&Foo::bar,  void>,        "");
      static_assert(test<&Foo::par,  int>,         "");
      #endif
      

      出于某种奇怪的原因,它适用于所有编译器,但不适用于 gcc 11.1 或更高版本。

      https://godbolt.org/z/4e7oKbod1

      【讨论】:

        猜你喜欢
        • 2011-12-29
        • 1970-01-01
        • 1970-01-01
        • 2013-11-23
        • 2021-09-20
        • 1970-01-01
        • 2020-06-15
        相关资源
        最近更新 更多