尾端返回类型


来看看另一个函数模版的例子:

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

template <typename T>
auto addThese(T begin, T end) {
    auto r = *begin;
    for(auto it = begin + 1; it != end; it++) {
        r += *it;
    }
    return r;
}

int main() {
    vector<int> vt = {1, 2, 3};
    cout << addThese(vt.begin(), vt.end()) << endl;

    vector<string> vt2 = {"Justin", "Monica", "Irene"};
    cout << addThese(vt2.begin(), vt2.end()) << endl;   

    return 0;
}

addThese返回迭代器范围内元素的累加,就范例来说,因为vtvector<int>,因此元素类型是int,现在问题来了,如果想在标头文件定义函数原型呢?这样行不通:

template <typename T>
auto addThese(T begin, T end);

因为没有代码上下文,是要怎么auto返回类型呢?就范例来说,*begin的类型就是int,那么这样呢?

template <typename T>
decltype(*begin) addThese(T begin, T end);

这样也行不通,因为编译器解析代码到decltype(*begin),根本还没看到begin参数,像这种情况,可以使用尾端返回类型(tailing return type):

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

template <typename T>
auto addThese(T begin, T end) -> decltype(*begin + *end);

int main() {
    vector<int> vt = {1, 2, 3};
    cout << addThese(vt.begin(), vt.end()) << endl;

    return 0;
}

template <typename T>
auto addThese(T begin, T end) -> decltype(*begin + *end) {
    auto r = *begin;
    for(auto it = begin + 1; it != end; it++) {
        r += *it;
    }
    return r;
}

在范例中,编译器已经看到了参数beginend,之后使用-> decltype(*begin + *end)就没问题,那为什么不用decltype(*begin)呢?

因为*begin是个 lvalue,若迭代器中的元素类型是E,那decltype(*begin)会推断出E&,这样的话,返回类型会参考局部变量r,然而函数执行完后r就无效了,因此不能使用decltype(*begin);这边需要的是个 rvalue,以令其推断出E,因此使用decltype(*begin + *end)

尾端返回类型并不是只能用在模版,有些偏好将函数返回类型写在最后的开发者,会特意这么编写函数:

auto add(int a, int b) -> int {
    return a + b;
}

另一个会运用到的场合,可能是在遇到底下的情况时:

class Foo {
public:
    class Orz {

    };

    Orz* orz();
};

Foo::Orz* Foo::orz() {
    return new Foo::Orz();
}

之后会谈到类定义,简单来说,这边定义了一个嵌套类,在实现orz成员函数时,必须得以Foo::Orz*来指明返回类型,因为必须知道是在哪个类中的内部类,然而,可以使用尾端返回类型来简化:

class Foo {
public:
    class Orz {

    };

    Orz* orz();
};

auto Foo::orz() -> Orz* {
    return new Foo::Orz();
}

因为auto后的Foo::已经指明了外部类了,尾端返回类型时就可以直接指定Orz*,不用再加上Foo::

简单来说,尾端返回类型既有的类型,或者取用->前看过的相关名称,用以定义或简化返回类型。


展开阅读全文