2017-10-12 79 views
-2

我试图编译这段代码,这应该打印矢量的总和,在2017年VS为什么在此折叠实现中不能推导出模板参数?

#include <stdio.h> 
#include <functional> 
#include <vector> 
#include <iostream> 
template<typename F, typename T, typename K> 
//int fold(F fun, T acc, K v) get the same error below 
int fold(F fun, T acc, std::vector<K> v) 
{ 
    switch (v.empty()) 
    { 
    case true: return acc; 
    case false: return fold(fun, fun(*v.begin(), acc), { ++v.begin(), v.end() }); 
    } 
} 
int main() 
{ 
    std::vector<int> v = { 1, 2, 3, 4 }; 
    std::cout << fold([](int a, int b) {return a + b; }, 0, v); 
} 

它产生一个错误:

error C2783: 'int fold(F,T,std::vector<K,std::allocator<K>>)': could not deduce template argument for 'K'

为什么不能K推断到intstd::vector<int>在这里?如果我更换

template<typename F, typename T, typename K> 
int fold(F fun, T acc, std::vector<K> v) 

template<typename F, typename T> 
int fold(F fun, T acc, std::vector<int> v) 

那么它编译成功。

+1

你也不回在'情况下没有过错:'。打开编译器警告,也许不要在布尔值上使用'switch'。 'if(v.empty())返回acc;'更具可读性。 (或'return v.empty()?acc:fold(...') – Ryan

+0

另外,我不是C++专家,但是看起来它每次都复制向量的其余部分?为什么不接受迭代器而不是向量,比如'std :: fill',例如你想要返回'T'而不是'int'。 – Ryan

+0

@RyanThanks你的建议,但是我实际上返回acc以防错误,你能解释一下吗? – czxyl

回答

2
{ ++v.begin(), v.end() } 

这被解释为两个元素的初始化列表。采用初始化列表的构造函数被选中,而不是采用两个迭代器的构造函数。结果,你试图调用fold而不是使用原始矢量的缩小副本,而是使用两个完全不同的元素的矢量。但累加器参数与它们不匹配,所以模板参数推导失败。

要解决,有一个明确的建筑取代这个:也

std:::vector<K> { ++v.begin(), v.end() } 

,在false情况下使用return fold (...)

2

另一种方式 - 支持每种容器。 (更新支持的元组)

#include <functional> 
#include <vector> 
#include <iostream> 
#include <initializer_list> 
#include <algorithm> 
#include <tuple> 
#include <array> 
#include <utility> 
#include <type_traits> 
#include <numeric> 

template<typename F, typename T, typename Container> 
auto fold(F fun, T acc, Container&& v) 
{ 
    using value_type = std::decay_t<decltype(*std::begin(v))>; 
    return std::accumulate(std::begin(v), std::end(v), value_type(acc), std::forward<F>(fun)); 
} 

// spceial case for initializer_list as it can't be deduced from a braced initializer 
template<typename F, typename T, class V> 
auto fold(F fun, T acc, std::initializer_list<V> v) 
{ 
    using value_type = std::decay_t<decltype(*std::begin(v))>; 
    return std::accumulate(std::begin(v), std::end(v), value_type(acc), std::forward<F>(fun)); 
} 

template<typename F, typename T, class Tuple, std::size_t...Is> 
auto fold_tuple(F&& f, T acc, Tuple&& tuple, std::index_sequence<Is...>) 
{ 
    using expand = int[]; 
    void(expand{ 
     0, 
     (acc = f(acc, std::get<Is>(std::forward<Tuple>(tuple))), 0)...   
    }); 
    return acc; 
} 

template<typename F, typename T, class...Vs> 
auto fold(F&& f, T acc, std::tuple<Vs...> const& tuple) 
{ 
    using value_type = std::common_type_t<std::decay_t<Vs>...>; 
    using tuple_type = std::tuple<Vs...>; 
    constexpr auto element_count = std::tuple_size_v<tuple_type>; 
    return fold_tuple(std::forward<F>(f), value_type(acc), tuple, std::make_index_sequence<element_count>()); 
} 

template<typename F, typename T, class...Vs> 
auto fold(F&& f, T acc, std::tuple<Vs...> && tuple) 
{ 
    using value_type = std::common_type_t<std::decay_t<Vs>...>; 
    using tuple_type = std::tuple<Vs...>; 
    constexpr auto element_count = std::tuple_size_v<tuple_type>; 
    return fold_tuple(std::forward<F>(f), value_type(acc), std::move(tuple), std::make_index_sequence<element_count>()); 
} 

int main() 
{ 
    std::vector<int> v = { 1, 2, 3, 4 }; 
    std::cout << fold(std::plus<>(), 0, v) << std::endl; 

    std::cout << fold(std::plus<>(), 0, { 2, 4, 6, 8 }) << std::endl; 

    std::cout << fold(std::plus<>(), 0, std::array<double, 4>{ 2.1, 4.1, 6.1, 8.1 }) << std::endl; 

    const double xx[] = { 1, 2, 3, 4, 5.5 }; 
    std::cout << fold(std::plus<>(), 0, xx) << std::endl; 

    std::cout << fold(std::plus<>(), 0, std::make_tuple(1, 2, 3, 4, 5.5)) << std::endl; 

    int x = 6; 
    double y = 7.7; 
    long long z = 100; 

    std::cout << fold(std::plus<>(), 0, std::tie(x, y, z)) << std::endl; 

} 

预期输出:

10 
20 
20.4 
15.5 
15.5 
113.7 

http://coliru.stacked-crooked.com/a/2eb9df5e4f60258e

相关问题