С ++ как связать лямбда с переменным c и ссылками - PullRequest
0 голосов
/ 17 июня 2020

Я использую библиотеку, когда многие классы написаны с помощью метода init (), возвращающего логическое значение. Я прочитал, что это позволяет вернуть код ошибки. В сигнатуре нет ничего общего (параметры могут быть любыми), но тип возвращаемого значения почти всегда bool, а имя функции всегда одно и то же, init. Аргументы могут быть любыми.

Пример:

class Handle {
  Handle(const Handle&) = delete;
};

class BigResource {
  BigResource(const BigResource&) = delete;
};

class A {
  bool init(int, const BigResource&);
};

class B {
  bool init(unsigned int, unsigned int, Handle&);
};

class D {
  bool init();
}

class E {
  bool init(const std::string&, const std::string&);
}

/* ... All classes are also not copyable */

Первый вопрос: я хотел бы использовать эти классы с контейнером. Условием является то, что я предполагаю, что все вызовы init () должны возвращать истину, а не то, что они всегда возвращают истину, но программа не работает, если какая-либо инициализация не работает (неисправимая ошибка, например, плохой mallo c или актив не найден). Я попытался обернуть все эти классы в другой, который обрабатывает инициализацию автоматически, но это невозможно, поскольку оператор преобразования не работает, потому что эти классы не копируются.

Итак, мне удалось использовать std :: generate для инициализации контейнеров STL с вызовом init () для каждого элемента. Теперь какова текущая проблема: я хотел бы иметь общий c класс генератора или функтор для инициализации всех элементов с одним и тем же аргументом, который может обрабатывать любой тип, уважающий ссылочные аргументы? std::bind передаются по значениям, и я не знаю, можно ли использовать что-то вроде std::ref с переменным c, при этом некоторые аргументы передаются по значению, а некоторые по ссылке.

What I пробовал сделать:

    /**
     * Functor to achieve the same effect but with a functor and stored arguments
     */
    template<typename T> struct Creator
    {
        template<typename... Args>
        Creator(Args&&... createArgs)
        {
            //m_callCreate = std::bind(create<T, Args...>, std::placeholders::_1, std::forward<Args>(createArgs)...); First test

            m_callCreate = [&](T &obj) {
                obj.init(std::forward<Args>(createArgs)...);
            };
        }

        void operator()(T& t) {
            m_callCreate(t);
        }

    private:
        std::function<void(T&)> m_callCreate;
    };

Работает? Я чувствую, что лямбда-захват приводит к висящей ссылке. Я пробовал также другие возможности (сохранить кортеж или std :: bind, завернутый в std :: function, которые выглядят так, как будто легко обертывают все аргументы, но я не знаю, какой метод лучше, и даже если это не UB. ) Я не очень понимаю эти функции и все возможные варианты поведения, но вкратце я хотел бы иметь такое же поведение, как если бы метод init был вызван напрямую, а именно:

  • когда параметр ссылку, используйте ссылку (указанный объект должен существовать до последнего вызова operator ())
  • , когда параметр является значением, и я чувствую, что вот настоящая проблема, захват по значению, например int (исходный объект может быть освобожден в конце конструктора)

Желаемое поведение:


// This code may be not possible

Handle handle;
std::vector<B> v(10, {1920, 1080, handle});


// Or at least:

Handle handle;
Generator<B> generator(1920, 1080, handle);
std::vector<B> v(10);
std::generate(v.begin(), v.end(), generator);


1 Ответ

0 голосов
/ 17 июня 2020

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

В этом отношении вы можете попробовать две вещи:

  1. Наследовать от этих классов вместо того, чтобы держать их в качестве члена в классе-оболочке:
    class BigResource;
    class A;
    class MyA : A {
        A wrapped;
    public:
        MyA() : wrapped() {
            auto result = wrapped.init();
            if (not result) { throw std::logic_error("Failed initializing A"); }
        }
        // more constructors
        // etc. etc
    }
    
  2. Используйте конструкторы перемещения вместо конструкторов копирования на BigResource и Handle
...