如何让编译器推断出c++ 11中模板化方法的返回类型

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

本文关键字:方法 返回类型 编译器 推断出 c++      更新时间:2023-10-16

我有一个模板方法,其中返回类型是reinterpret_cast<>()调用的结果。

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

这种方式使我在调用这个函数时使用<> -语法:

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

我更倾向于不带模板参数调用这个方法,让编译器根据变量类型推断返回类型。

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

对于返回类型演绎是否可能?

我尝试使用auto, -> -操作数和尾部返回类型语法

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

但是它仍然不能工作。

在c++ 11中有什么办法做到这一点吗?

可以,但只能通过具有转换函数模板的代理类型:

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

的例子。

注意,熟悉使用auto进行返回类型推断的用户可能会对这种技术感到困惑:

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

直到c++ 17,你可以通过给BufferProxy一个删除的复制构造函数来防止auto out = a.buffer();编译(也许通过聚合构造:return {_ptr};返回它),但是用户仍然可以使用auto&&,并且从c++ 17开始保证复制省略将使auto形式再次工作。

您可能想要一个像下面这样的类。这似乎提供了你想做的大部分事情。

我想知道的一个问题是如何确定存储在类中的指针是否为相同类型。因此,我认为最好添加一个额外的方法来使用hash_code()方法检查typeid()

所以我想到的类使用operator的想法@ecatmur在他/她的答案:

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 */; }
};

相等操作符可以像上面那样只检查类型,或者如果取消了额外的检查注释,也可以检查指针的值。您可能只是想检查类型。

我用了一个简单的演示函数来测试它,如下所示:
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;
}

我用调试器Visual Studio 2013完成了这个步骤,看起来它像一个冠军。

我想这个答案是最优雅的。

无论如何,你也可以让类像这样初始化你的指针:

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

这种方式的类型是从参数列表中推断出来的,您不必显式指定它。