Non-static data members class deduction

Viewed 165

I'm trying to solve type deduction issue.

Here's the demo code, that uses function overloading to define, whether the passed variable is int or double


std::string tcast( const double &x){ return "Floating Point";};
std::string tcast( const int    &x){ return "Integer";};

int main(){
    double dA;
    int    iB;
    std::cout << "Access to variable type \n";
    std::cout << "dA : "<< tcast( dA )<< std::endl;
    std::cout << "iB : "<< tcast( iB )<< std::endl;
}
Access to variable type 
dA : Floating Point
iB : Integer

But this function doesn't work for non-static class members:

struct Foo{
    double dA;
    int    iB;
};

int main(){

    std::cout << "Member dA : "<< tprint( &Foo::dA )<< std::endl;
    std::cout << "Member iB : "<< tprint( &Foo::iB )<< std::endl;

}

GCC compilation error:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:111:44: error: no matching function for call to ‘tcast(double Foo::*)’
  111 |     std::cout << "dA : "<< tcast( &Foo::dA )<< std::endl;
      |                                            ^
main.cpp:96:13: note: candidate: ‘std::string tcast(const double&)’
   96 | std::string tcast( const double &x){ return "Floating Point";};
      |             ^~~~~
main.cpp:96:34: note:   no known conversion for argument 1 from ‘double Foo::*’ to ‘const double&’
   96 | std::string tcast( const double &x){ return "Floating Point";};
      |                    ~~~~~~~~~~~~~~^
main.cpp:97:13: note: candidate: ‘std::string tcast(const int&)’
   97 | std::string tcast( const int    &x){ return "Integer";};
      |             ^~~~~
main.cpp:97:34: note:   no known conversion for argument 1 from ‘double Foo::*’ to ‘const int&’
   97 | std::string tcast( const int    &x){ return "Integer";};
      |                    ~~~~~~~~~~~~~~^

But the following code compiles :

template <typename T>
std::string tprint( const T    &x){ return typeid(x).name();};
...
std::cout << "dA : "<< tprint( dA )<< std::endl;
std::cout << "iB : "<< tprint( iB )<< std::endl;
std::cout << "Member dA : "<< tprint( &Foo::dA )<< std::endl;
std::cout << "Member iB : "<< tprint( &Foo::iB )<< std::endl;

And so it's visible for compiler that dA has type double and iB int

dA : d
iB : i
Member dA : M3Food  // ends in d
Member iB : M3Fooi  // ends in i

So what should be the right way to deduct the type of the non-static data member?

2 Answers

We can use template argument deduction to our advantage here. Pointer to member data's syntax is

DataType Class::*Pointer

After deducing the member type we can call the required overload.

template<typename Class, typename MemType>
std::string tcast(MemType Class::*mData){
    return tcast(MemType{});
}

Link to the code in godbolt

You might add extra overload:

template <typename C>
std::string tprint(double C::*){ return "member Floating Point";};

template <typename C>
std::string tprint(int C::*){ return "member Integer";};

Demo

Related