Pull to refresh

Comments 17

UFO just landed and posted this here
> можно ли сотворить подобное, не прибегая к помощи лямбд и C++11?

Просто замените лямбды на function objects с виртуальными методами.
Приведённый класс VectorWrapper каким-нибудь существующим cpp-компилером компилируется?
Мне вообще шаблоны из-за таких велосипедов и не нравятся. Чем плохи обычные интерфейсы? На интерфейс глянул — и понятно, что нужно передавать. А тут сколько всего нужно прочитать и понять, какие методы должен реализовывать объект-параметр.
true-cpp way это сделать функцию fn() шаблонной.
Но поскольку не хочется, не обойтись без вызова функции по указателю (будь это virtual или lambda).
Вот, собственно получилось полностью сохранить семантику VectorWrapper:

Скрытый текст
#include <vector>
using namespace std;

template <typename T>
class IVectorWrapper
{
public:
    virtual T& operator[](size_t i) = 0;
    virtual size_t size() = 0;
    virtual ~IVectorWrapper() { }
};

template <typename T, typename C>
class KnownVectorWrapper : public IVectorWrapper<T>
{
public:
    KnownVectorWrapper(C& _container) : container(_container) { }

    T& operator[](size_t i)
    {
        return container[i];
    }

    size_t size()
    {
        return container.size();
    }

private:
    C& container;
};

template <typename T>
class VectorWrapper : IVectorWrapper<T>
{
public:
    template <typename C>
    VectorWrapper(C& _container) : container(new KnownVectorWrapper<T,C>(_container)) { }
    ~VectorWrapper() { delete container; }

    T& operator[](size_t i)
    {
        return (*container)[i];
    }

    size_t size()
    {
        return container->size();
    }

private:
    IVectorWrapper<T>* container;
};


void fn(VectorWrapper<int> x)
{
    int a = 0;
    for (size_t i = 0; i < x.size(); ++i)
    {
        printf("%d ", x[i]);
    }
}

void main()
{
        vector<int> a;
        a.push_back(7);
        a.push_back(15);
        fn(a);
}
Можно избавиться от new/delete, явно перечислив все возможные контейнеры (их не должно быть много, поэтому не сложно в одном классе это сделать. Также добавляется небольшой оверхед по памяти: sizeof(size_t) на каждый контейнер).

template <typename T>
class VectorWrapper
{
public:
    VectorWrapper(vector<T>& _container) : sv(_container), container(&sv) { }
    VectorWrapper(QList<T>& _container) : qv(_container), container(&qv) { }

    T& operator[](size_t i) { return (*container)[i]; }
    size_t size() { return container->size(); }

private:
    KnownVectorWrapper<T,vector<T>> sv;
    KnownVectorWrapper<T,QList<T>> qv;
    IVectorWrapper<T>* container;
};
* не указал, что теперь нужен конструктор без параметров
template <typename T, typename C>
class KnownVectorWrapper : public IVectorWrapper<T>
{
public:
    KnownVectorWrapper() : container(*(C*)NULL) { }
    KnownVectorWrapper(C& _container) : container(_container) { }
    // ...
Да, нужно переписать класс. Если есть сценарии, когда ссылка не инициализирована, лучше использовать указатель.
Практически, инициализация неиспользуемых ссылок dereferenced NULL-ом никогда не вызывала проблем.
$ cat /tmp/zzz.cc 
int main(int argc, char *argv[]) {
  int i;
  int &x = *(int *) (argc == 2 ? &i : 0);
}
$ clang /tmp/zzz.cc
$ ./a.out 
$ clang -fcatch-undefined-behavior /tmp/zzz.cc
$ ./a.out 
Illegal instruction
угловые скобки пропали в строке
KnownVectorWrapper < T, QList < T > > qv;
Всякий раз, когда вам нужны функции из C++11 (или даже не вошедшие в него), но нет возможности использовать C++11, смотрите буст. По лямдам, по именованным аргументам, по статическим assert-ам, по хитрым контейнерам и алгоритмам, по потокам, по регулярным выражениям и по концептам в том числе. Тем более ключевое слово «концепт» вы знаете. www.boost.org/doc/libs/1_51_0/libs/concept_check/concept_check.htm. Там же есть описание реализации.

Идея похожа на то что есть в этом докладе (самое начало пример с Point). Там докладчик назвал это SFINAE, только здесь очень сильно упрощенная.

Это конечно круто выглядит (передаем практически любой объект, а функция такая умная что сама определит как с ним правильно работать), но в реальности из-за подобных механизмов очень сильно страдает код, он становится сильно раздутым, непонятным, если описать одним слово — не продакшен (это приминительно к тому что в том докладе, в топике код очень простой).

А второе, я пока не видел ни одного реального примера где бы подобные механизмы реально были бы нужны — это применительно и к топику и докладу (были бы нужны — повышали производительность, читаемость, сопровождаемость кода, вообщем делали бы код лучше). В С++ подобные вещи (указанные в топике) вынесены в алгоритмы, где мы оперируем итераторами и подобный код обычно пишется так:

template <typename Container>
void fn(Container & c)
{
    for (auto & i : c)
    {
        doSomething(i);
    }
}

::std::vector<int> sv;
QList<int> qv;
OtherSuperVector<int> ov;

fn(sv);
fn(qv);
fn(ov);
На C++03 можно переделать так:

template <typename T>
class VectorWrapper
{
public:

    template <typename C>
    VectorWrapper(C& container) :
    _getter(boost::bind(&C::operator[], &container, _1)),
    _sizeGetter(boost::bind(&C::size, &container))
    {
    }

    T& operator[](size_t i)
    {
        return _getter(i);
    }

    size_t size()
    {
        return _sizeGetter();
    }

private:
    boost::function<T&(size_t) > _getter;
    boost::function<size_t() > _sizeGetter;
};
Всем спасибо за примеры, узнал немного нового…
Sign up to leave a comment.

Articles