C ++ 0x模板函数对象推断

时间:2010-09-28 05:17:56

标签: c++ templates functional-programming function-pointers c++11

我是一名Scala / Java程序员,希望将自己重新引入C ++并学习C ++ 0x中的一些令人兴奋的功能。我想从设计我自己的稍微功能的集合库开始,基于Scala的集合,这样我就可以深入理解模板。我遇到的问题是编译器似乎无法推断模板化函数对象的任何类型信息。

FC++似乎已经使用“签名”解决了这个问题。这些看起来与result_type typename非常相似,我想我会使用新的函数语法来实现它。任何人都可以提出一种在C ++ 0x中做这种事情的方法,如果可能的话,或者至少解释一下FC ++是如何实现这一目标的?这是我正在玩的代码片段

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

template<class T>
class ArrayBuffer {
private:
    vector<T> array;
public:
    ArrayBuffer();
    ArrayBuffer(vector<T> a) : array(a) {}

    template<typename Fn>
    void foreach(Fn fn) {
        for(unsigned int i = 0; i < array.size(); i++) fn(array[i]);
    }

    template<typename Fn>
    auto map(Fn fn) -> ArrayBuffer<decltype(fn(T()))> {
        vector<decltype(fn(T()))> result(array.size());
        for(int unsigned i = 0; i < array.size(); i++) result[i] = fn(array[i]);
        return result;
    }
};

template<typename T>
class Print {
    public:
    void operator()(T elem) { cout<<elem<<endl; }
};

template<typename T>
class Square{
public:
    auto operator()(T elem) -> T {
        return elem * elem;
    }
};

int main() {
    vector<int> some_list = {5, 3, 1, 2, 4};
    ArrayBuffer<int> iterable(some_list);
    ArrayBuffer<int> squared = iterable.map(Square<int>()); // works as expected
    iterable.foreach(Print<int>()); // Prints 25 9 1 4 16 as expected
    iterable.foreach(Print()); // Is there a way or syntax for the compiler to infer that the template must be an int?
    ArrayBuffer<int> squared2 = iterable.map(Square()); // Same as above - compiler should be able to infer the template.
}

3 个答案:

答案 0 :(得分:5)

您也可以将operator()设为模板

class Print {
    public:
    template<typename T>
    void operator()(T elem) { cout<<elem<<endl; }
};

然后你可以通过Print()。要传递ArrayBuffer<decltype(fn(T()))>中的参数,我建议您使用declval,这样您也可以使用非默认的可构造T

ArrayBuffer<decltype(fn(declval<T>()))>

答案 1 :(得分:1)

您似乎正在重塑C ++标准库。我不是在谈论你的容器 C ++已经功能齐全。

我认为您缺少关于C ++标准库的几个关键点。

  • 首先是 Generic ,然后是面向对象 如果一个algrithm可以通用的方式实现,它将不包含在该类中 ArrayBuffer::foreach == std::for_each
    ArrayBuffer::map == std::transform
  • 标准算法适用于迭代器而不是完整的容器。新的C ++程序员经常会错过这一点,因为Java和C#都缺乏这个概念。迭代器比单独的容器更具表现力/灵活性。迭代器是arguably要走的路。也就是说,Ranges表达迭代器是一种更简洁的方式(范围只是成对的迭代器)。

这是一个在功能上使用C ++的示例。这也是C#没有使用迭代器的一个很好的例子。尽管它们非常强大,但它们的冗长程度令C#的目标受众感到恐惧。 Java不使用迭代器,因为它们不是面向对象,语言设计者在开始时就真的很肛门。

struct Print
{
   template<typename T>
   void operator()( const T& t )
   { std::cout << t << std::endl; }
};

struct Squared
{
   template<typename T>
   T operator()( const T& t )
   { return t*t; }
};

int main()
{
   std::vector<int> vi;
   std::foreach( vi.begin(), vi.end(), Print());
   std::foreach( vi.begin(), vi.end(), [](int i){ std::cout<<i<<std::endl; } );

   std::vector<int> vi_squared;

   std::transform( vi.begin(), vi.end(), std::back_inserter(vi_squared), Squared() );
   // or
   vi_squared.resize( vi.size() );
   std::transform( vi.begin(), vi.end(), vi_squared.begin(), Squared() );
}

答案 2 :(得分:0)

  

编译器是否有方法或语法推断模板必须是int?

问题是模板不需要是一个int 这同样有效。

iterable.foreach(Print<float>());

你真正要问的是。
我可以使模板仿函数具有不同的默认值,具体取决于使用它的上下文吗?

答案是否定的。我想我们理论上可以为简单的情况做这件事。然而角落的情况很快就会使这种情况变得不可行。该函数的真正上下文是完整的编译单元,而不仅仅是它实例化的行。