简体   繁体   中英

c++ : Universal getter for class

I kind of need help! I want to define a template method for my class to access its private fields. Here is my code:

#include <string>
#include <vector>
using namespace std;

class ex
{
public:
    ex(string pegah_,int amin_):pegah(pegah_),amin(amin_){}
    template<typename T>
    T get_field(){
        if(is_same<T,string>::value)
            return pegah;
        else if(is_same<T,int> ::value)
            return amin;
    }
private:
    string pegah;
    int amin;
};

int main(void)
{
    string i = "salam";
    int o=10;
    ex y(i,o);
    y.get_field<string>();
}

as you see I want to use just one function. But I keep getting this error:

test.cpp: In instantiation of ‘T ex::get_field() [with T = std::basic_string<char>]’:
test.cpp:30:21:   required from here
test.cpp:15:8: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
 return amin;
        ^
In file included from /usr/include/c++/4.8/string:52:0,
                 from test.cpp:1:
/usr/include/c++/4.8/bits/basic_string.h:490:7: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
       basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());

can anyone help?

Instead you could lay your code out like this:

template<typename T> T get_field();

// outside the class:
template<> inline int ex::get_field<int>() { return amin; }
template<> inline string ex::get_field<string>() { return pegah; }

As you have it now, all branches of the if..else must compile.

Basically you have three options to do it.

First using explicit specialization of template member function.

class Foo {
public:
    template <typename T>
    T get () const;

private:
    std::string str {"XXX"};
    int value {42};
};


template <>
inline std::string Foo::get () const {
    return str;
}

template <>
inline int Foo::get () const {
    return value;
}

Second one is to use helper function with different parameters type.

class Foo2 {
public:
    template <typename T>
    T get () const {
        return get_helper (typename std::is_same<T, std::string>::type {});
    }


private:
    std::string get_helper (std::true_type) const {
        return str;
    }

    int get_helper (std::false_type) const {
        return value;
    }

private:
    std::string str {"YYY"};
    int value {44};
};

Third option is to use SFINAE.

class Foo3 {
public:
    template <typename T>
    typename std::enable_if<std::is_same<T, std::string>::value, T>::type get () const {
        return str;
    }

    template <typename T>
    typename std::enable_if<std::is_same<T, int>::value, T>::type get () const {
        return value;
    }

private:
    std::string str {"ZZZ"};
    int value {45};
};

and usage would be like:

template <typename T>
void show (T v) {
    std::cout << v << std::endl;
}

Foo f1;
show (f1.get<std::string> ());
show (f1.get<int> ());

Foo2 f2;
show (f2.get<std::string> ());
show (f2.get<int> ());

Foo3 f3;
show (f3.get<std::string> ());
show (f3.get<int> ());

Second option is helpful when you want to distinguish between two types. If you have more getters, then probably you will need to use first or third option.

I think it is better you define a getter and setter for each field. That is a better approach. It's easier to read and to understand and you achieve the same as with the template technique.


Explanation of your code:

It does not compile because of type checking. Template functions are generated when used in C++11. You use it with template parameter string so the function is generated. The problem is that you generate a function that returns T as a string , but you have code in your function that returns int (variable amin ). Generate the function in your mind like so for T equals string :

string get_field(){
    if(is_same<string,string>::value)
        return pegah;                      // OK
    else if(is_same<string,int> ::value)
        return amin;                       // NOT OK, amin is of type int
}

One solution is that of MM , it's called specialization. You specialize a template for (a) specific argument(s). And there are also other answers coming up.


I do not recommend that, because you finally do nothing else but generating getter functions for each variable in a specialized template. You could as well just have written:

string get_pegah(){ return pegah; }
int get_amin() { return amin; }

Easier to read, to maintain and straight forward. And more efficient I think.


as you see I want to use just one function

You don't really. You either call get_field<string> or get_field<int> and when called the appropriate function would be generated; either with T=string , T=int or both (depending on your use case). Though as you have learned by now, it's an error to do so in that case.

What you probably meant was that you want to have one function definition to do what you want. I don't think that is possible.

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