为什么不能在此折叠实现中推导出模板参数?

时间:2017-10-12 03:15:08

标签: c++ functional-programming

我正在尝试编译这段代码,它应该在VS 2017中打印一个向量的总和。

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

产生错误:

  

错误C2783:'int fold(F,T,std :: vector&lt; K,std :: allocator&lt; K&gt;&gt;)':无法推断'K'的模板参数

为什么Kint无法推断std::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)

然后它成功编译。

2 个答案:

答案 0 :(得分:2)

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

这被解释为两个元素的初始化列表。选择采用初始化列表的构造函数,而不是采用两个迭代器的构造函数。因此,您尝试使用原始矢量的简化副本调用折叠,但使用两个完全不同的元素的矢量。但是累加器参数与它们不匹配,因此模板参数推断失败。

要修复,请使用明确的结构替换它:

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

另外,在return fold (...)案例中使用false

答案 1 :(得分: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