【问题标题】:Why can’t a template argument be deduced in this fold implementation?为什么在这个折叠实现中不能推导出模板参数?
【发布时间】:2018-03-23 20:57:41
【问题描述】:

我正在尝试在 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>)': 无法推导出 'K' 的模板参数

这里为什么不能将K 推导出为intstd::vector&lt;int&gt;?如果我替换

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)

然后编译成功。

【问题讨论】:

  • 您没有返回 case false: 中的任何内容。打开编译器警告,也许不要在布尔值上使用switchif (v.empty()) return acc; 更具可读性。 (或return v.empty() ? acc : fold(…
  • 另外,我不是 C++ 专家,但这看起来每次都复制向量的其余部分?为什么不接受迭代器而不是向量,例如std::fill?你会想要返回 T 而不是 int
  • @Ryan感谢您的建议。但我实际上返回 acc 以防万一。你能再解释一下吗?
  • @Ryan 我这里只是想模拟一个sml函数折叠所以没注意效率
  • 为什么不直接使用std::accumulate?它的作用与您的 fold 函数应该做的完全一样,但它适用于任何集合,并且不会制作一堆不必要的集合副本。

标签: c++ functional-programming


【解决方案1】:
{ ++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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      • 2018-12-09
      • 1970-01-01
      • 1970-01-01
      • 2011-08-28
      相关资源
      最近更新 更多