简体   繁体   中英

How to have the compiler deduce the return type of a templated method in C++11?

I have a templated-method where the return-type is will be the result of a reinterpret_cast<>()-call.

class A {
    void *_ptr;
public:
    template<typename T>
    T buffer() { return reinterpret_cast<T>(_ptr); }
};

This way makes me use the <> -syntax when calling this function:

A a;
auto b = a.buffer<double *>();

I'd prefer to call this method without the template arguments and let the compiler deduce the return type, based on the variable-type.

A a;
double *out = a.buffer();

Is this possible with return-type deduction?

I tried using auto , the -> -operand and the trailing return type syntax .

auto buffer() -> decltype(reinterpret_cast<T>(_ptr)) const 
     { return reinterpret_cast<T>(_ptr); }

but it still doesn't work.

Is there any way doing this, in C++11?

Yes, but only via a proxy type having a conversion function template:

struct BufferProxy {
  void* ptr;
  template<class T> operator T*() { return reinterpret_cast<T*>(ptr); }
};
BufferProxy buffer() { return BufferProxy{_ptr}; }

Example .

Note that users who have become familiar with the use of auto for return type deduction are likely to become confused by this technique:

auto out = a.buffer(); // out is BufferProxy 
auto* out = a.buffer(); // fails to compile; can't deduce 'auto*' from 'a.A::buffer()'

Up until C++17, you can prevent auto out = a.buffer(); from compiling by giving BufferProxy a deleted copy constructor (and perhaps returning it by aggregate construction: return {_ptr}; ), but the user could still use auto&& and from C++17 guaranteed copy elision will make the auto form work again.

You may want a class something like the following. This would seem to offer most of what you want to do.

One issue I was wondering about was how to determine if a pointer stored into the class was the same type or not. So I thought it would be best to add an additional method to check the typeid() using the hash_code() method.

So the class I came up with using the operator idea of @ecatmur in his/her answer:

class A {
    void    *_ptr;
    size_t  _ptrHash;

public:
    template<typename T> operator T*() { return reinterpret_cast<T *>(_ptr); }
    template<typename T>
    void SetPtr(T *p) { _ptr = p; _ptrHash = typeid(*p).hash_code(); }
    template<typename T> bool operator == (T *p) { return p && typeid(*p).hash_code() == _ptrHash /* && p == _ptr */; }
};

The equality operator could either check only the type as above or if you uncomment the additional check, also check for value of the pointer. You probably just want to check for the type.

A simple demo function that I used to test this out was as follows:

void funky1() {
    A a;

    double ddd[50] = { 0.0 };
    ddd[0] = 5.0; ddd[2] = 7.0;

    a.SetPtr(&ddd[0]);

    double *p = a;
    bool bb = a == p;

    long  lll[50] = { 0 };
    lll[0] = 5; lll[2] = 7;

    long *q = a;
    bb = a == q;

    a.SetPtr(&lll[0]);
    q = a;

    bb = a == q;
}

I stepped through this with the debugger, Visual Studio 2013, and it looked like it worked like a champ.

I guess this answer is the most elegant.

Anyway, you can also let the class initializes your pointer as it follows:

class A {
    void *_ptr;
public:
    template<typename T>
    void buffer(T **t) { *t = reinterpret_cast<T*>(_ptr); }
};

int main() {
    A a;
    double *b;
    a.buffer(&b);
}

This way the type is deduced from the parameter list and you have not to explicitly specify it.

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