Home > Mobile >  Converting a pointer-to-member type to a simple pointer type
Converting a pointer-to-member type to a simple pointer type

Time:09-25

I have the following type, which I get with decltype

QString UserInfo::*&

I can remove the & part by wrapping decltype with std::remove_reference_t but I also want to remove UserInfo::* part

How can I do that so I can use only QString type in my templates

I'm using this template in initializer list where I don't have access to solid object or this pointer to .* operator in decltype

CodePudding user response:

Using a valid object is not necessary in unevaluated contexts (like decltype). To exaggerate a little, you could even dereference a null pointer in there, and nothing bad would happen, since the dereference is never actually evaluated.

To create an object of a type that is not valid, but can be used in unevaluated contexts, you can use std::declval.

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

CodePudding user response:

You can write a trait to extract the type of the member from the member function pointer:

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

CodePudding user response:

Use template specialization to extract member type:

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

CodePudding user response:

Here is my version using C 17 in such way that decltype is not needed when template is used (a bit more handy):

#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

For some strange reason works on all compilers, but not for gcc 11.1 or newer.

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

  • Related