Zhou
Zhou

Reputation: 773

How do I call template array operator overloading function?

I need to create an adapter C++ class, which accepts an integer index, and retrieves some types of data from a C module by the index, and then returns it to the C++ module.

The data retrieving functions in the C module are like:

int getInt(int index);
double getDouble(int index);
const char* getString(int index);
// ...and etc.

I want to implement an array-like interface for the C++ module, so I created the following class:

class Arguments {
public:
    template<typename T> T operator[] (int index);
};

template<> int Arguments::operator[] (int index) { return getInt(index); }
template<> double Arguments::operator[] (int index) { return getdouble(index); }
template<> std::string Arguments::operator[] (int index) { return getString(index); }

(Template class doesn't help in this case, but only template member functions)

The adapter class is no biggie, but calling the Arguments::operator[] is a problem!

I found out that I can only call it in this way:

Arguments a;
int i = a.operator[]<int>(0);       // OK
double d = a.operator[]<double>(1); // OK

int x = a[0];                       // doesn't compile! it doesn't deduce.

But it looks like a joke, doesn't it?

If this is the case, I would rather create normal member functions, like template<T> T get(int index).

So here comes the question: if I create array-operator-overloading function T operator[]() and its specializations, is it possible to call it like accessing an array?

Thank you!

Upvotes: 0

Views: 131

Answers (1)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122228

The simple answer is: No, not possible. You cannot overload a function based on its return type. See here for a similar quesiton: overload operator[] on return type

However, there is a trick that lets you deduce a type from the lhs of an assignment:

#include <iostream>
#include <type_traits>

struct container;
struct helper {
    container& c;
    size_t index;
    template <typename T> operator T();
};

struct container {
    helper operator[](size_t i){
        return {*this,i};
    }
    template <typename T> 
    T get_value(size_t i){
        if constexpr (std::is_same_v<T,int>) {
            return 42;
        } else {
            return 0.42;
        }
    }
};

template <typename T> 
helper::operator T(){
    return c.get_value<T>(index);
}

int main() {
    container c;
    int x = c[0];
    std::cout << x << "\n";
    double y = c[1];
    std::cout << y ;
}

Output is:

42
0.42

The line int x = c[0]; goes via container::get_value<int> where the int is deduced from the type of x. Similarly double y = c[1]; uses container::get_value<double> because y is double.

The price you pay is lots of boilerplate and using auto like this

auto x = c[1];

will get you a helper, not the desired value which might be a bit unexpected.

Upvotes: 2

Related Questions