专门基于std :: begin()返回类型的尾随返回类型

时间:2014-04-03 09:04:01

标签: templates visual-studio-2012 c++11 sfinae trailing-return-type

作为一个练习,我想为容器创建一个通用的模板化求和函数。虽然使用“嵌套模板”重载可以很好地实现我的目标,但我想编写一个仅基于std::begin() / std::end()的函数,以允许它处理那些为这些函数带来重载的内容。并利用C ++ 11 SFINAE的全部功能。

然而,我遇到了返回类型的问题。这是我尝试过的,用数字容器的通用“sum”函数说明:

首先,使用代码:

int arr[] = {1,2,3,4};  
vector<int> vec(begin(arr),end(arr));
auto sum1 = sum(vec);
auto sum2 = sum(arr);

这是我对函数的期望 - 从容器和数组开始工作。

这是我尝试解决问题的方法:

template<typename T> auto sum(const T& container) -> decltype(*begin(container)) {
    typedef remove_const<remove_reference<decltype(*begin(container))>::type>::type val_type;
    val_type sum;
    if (is_arithmetic<val_type>::value)
        sum = 0;
    for (const auto& value: container)
        sum = sum + value;
    return sum;
}

这有什么问题:它有效,但会发出警告(C4172: returning address of local variable or temporary)。这显然是一件坏事,基本上会产生未定义的行为。

我尝试通过以下方式定义尾随返回类型:

1. var_type的复制声明

template<typename T> auto sum(const T& container) ->
    remove_const<remove_reference<decltype(*begin(container))>::type>::type

这会产生错误:C2923: 'std::remove_const' : 'std::remove_reference<_Ty>::type' is not a valid template type argument for parameter '_Ty'(很奇怪,因为同样适用于val_type

2.使用容器[0]

template<typename T> auto sum(const T& container) -> decltype(container[0])

这样做有效,但它对container类型产生了额外的限制,我想避免这种限制,并为C4172提供vector

3.尝试仅删除参考

template<typename T> auto sum(const T& container) -> remove_reference<decltype(*begin(container))>::type

结果:error C2061: syntax error : identifier 'type'

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

这很简单:decltype(*std::begin(container))已解决T const &。 应用与val_type相同的操作,添加一些typename个关键字,这样就可以了。

为什么typename是强制性的?看看错误:

error:   expected a type, got ‘std::remove_reference<decltype (* std::begin(container))>::type’

如果您尝试在此表达之前添加typename,您现在拥有:

error: need ‘typename’ before
    ‘std::remove_const<typename std::remove_reference<decltype (* std::begin(container))>::type>::type’
because
    ‘std::remove_const<typename std::remove_reference<decltype (* std::begin(container))>::type>’
is a dependent scope

GCC 4.7.2的工作示例:

#include <iostream>
#include <type_traits>
#include <vector>

using namespace std;

template <class T>
auto sum (T const & container)
-> typename remove_const<typename remove_reference<decltype(*begin(container))>::type>::type
{
    typedef typename remove_const<typename remove_reference<decltype(*begin(container))>::type>::type val_type;
    val_type sum;
    if (is_arithmetic<val_type>::value)
        sum = 0;
    for (const auto& value: container)
        sum = sum + value;
    return sum;
}

int main ()
{
    vector<int> v { 0, 1, 2, 3 };
    cout << sum(v) << endl;
}