模板,类型演绎不足

templates, insufficient type deduction

本文关键字:演绎 类型 模板      更新时间:2023-10-16

我正在玩Streams的一个Java 8实现。我希望编译器接受以下代码

Stream stream;
stream
    .map      ([] (int x)  { return 10*x; })      // error
    .forEach  ([] (int x)  { cout << x << " ";}); 

,但编译器(gcc版本4.9.2)拒绝它,并注明

template argument deduction/substitution failed:
‘main(int, char**)::<lambda(int)>’ is not derived from ‘std::function<Re(int)>’
   .map       ([] (int x)  { return 10*x; })
requires a type parameter for `map`

使用

可以编译(并且运行良好)
   .map<int>  ([] (int x)  { return 10*x; })

有希望摆脱<map>的东西吗?


下面是简化后的代码(有足够的声明)

#include <iostream>
#include <functional>
using namespace std;
template <typename Tfrom, typename Tto> class MappedStream;
template <typename T>
class Stream
{
  public:
    void forEach(function< void(T) > action) 
    {}
    template <typename Re>
    MappedStream<T,Re> map (function < Re(T)> mapping) {
        return MappedStream<T,Re>(*this, mapping);
    }
};
template <typename Tfrom, typename Tto>
class MappedStream
   : public Stream<Tto>
{   
  public:
    MappedStream(Stream<Tfrom> & source,
                function <Tto(Tfrom)> mapping)
    {}
};
int main(int argc, char **argv)
{   
  Stream<int> stream;
  stream
   .map<int> ([] (int x) { return 10*x; })
   //  XXXXX                          <- how to get rid of this?
   .forEach  ([] (int x) { cout << x << " ";});
   return 0;
}

lambda不是std::function,并且您几乎永远不会想要使用std::function</* something that contains a template parameter */>作为函数模板的参数,因为可以推断那里的模板参数的唯一方法是如果调用者构造std::function并将其传递进去。

相反,接受任何函数对象,然后找出返回类型:

template <typename F, typename Re = std::result_of_t<F&(T)>>
MappedStream<T,Re> map (F mapping) {
    return MappedStream<T,Re>(*this, mapping);
}
一般来说,应该避免不必要的类型擦除。对于MappedStream的类型来说,不依赖于所使用的映射器是很有用的,所以在里面存储std::function是合理的,但是forEach可能应该是一个接受任何函数对象的模板,而不仅仅是一个std::function