Вывод объекта шаблонной функции C ++ 0x - PullRequest
5 голосов
/ 28 сентября 2010

Я программист на Scala / Java, желаю вновь познакомиться с C ++ и изучить некоторые интересные функции в C ++ 0x.Я хотел начать с разработки моей собственной слегка функциональной библиотеки коллекций, основанной на коллекциях Scala, чтобы я мог получить полное представление о шаблонах.Проблема, с которой я сталкиваюсь, заключается в том, что компилятор, по-видимому, не может выводить какую-либо информацию о типах для объектов-шаблонов.

FC ++ , кажется, решил эту проблему с помощью «Подписи».Похоже, они действительно похожи на имя типа result_type, и я подумал, что получу это, используя новый синтаксис функции.Может кто-нибудь предложить способ сделать что-то подобное в 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 ]

5 голосов
/ 28 сентября 2010

Вы можете сделать 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 голос
/ 28 сентября 2010

Кажется, вы заново изобретаете стандартную библиотеку C ++.Я не говорю о ваших контейнерах.
C ++ уже достаточно функционально оборудован.

Я думаю, вы упускаете несколько ключевых моментов в стандартной библиотеке C ++.

  • Это универсальный первый и объектно-ориентированный второй.
    Если алгоритм может быть реализован универсальным образом, он не будет включен в класс.
    ArrayBuffer::foreach == std::for_each
    ArrayBuffer::map == std::transform
  • Стандартные алгоритмы работают на итераторах, а не на полных контейнерах.Это часто упускается новыми программистами C ++, потому что в Java и C # отсутствует концепция.Итераторы более выразительны / гибки, чем одни контейнеры.Итераторы возможно путь.Тем не менее, Диапазоны являются гораздо более кратким способом выражения итераторов (диапазон - просто парные итераторы).

Вот пример использования 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() );
}
0 голосов
/ 28 сентября 2010

Есть ли способ или синтаксис для компилятора сделать вывод, что шаблон должен быть int?

Проблема в том, что шаблону не нужно , чтобы быть целым числом.
Это в равной степени верно.

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

Что вы на самом деле спрашиваете?
Можно ли сделать так, чтобы у функтора шаблона было другое значение по умолчанию в зависимости от контекста, в котором он используется?

Ответ - нет. Я полагаю, что теоретически мы могли бы сделать это для простых случаев. Однако угловые случаи быстро делают это невозможным. Истинный контекст функции - это полная единица компиляции, а не просто строка, в которой она создается.

...