Невозможно переместить std :: function на карту - PullRequest
0 голосов
/ 13 марта 2019

У меня есть некоторый прототип класса Signal

#include <map>
#include <string>
#include <functional>

template <class T>
class Signal
{
public:
    std::function<T> slot;
};

Далее следует шаблон синглтона SignalCollection класса, который автоматически генерирует соответствующий тип для Signal

template <class T>
class SignalCollection
{
private:
    SignalCollection() {}
    SignalCollection(const SignalCollection&) = delete;
    SignalCollection& operator= (const SignalCollection&) = delete;
public:

    static SignalCollection& Instance()
    {
        static SignalCollection br{};
        return br;
    }

    std::map<std::string, T> signals_map;

    void add(T&& signal)
    {
        this->signals_map.insert(std::make_pair("a", std::forward<T>(signal)));
    }
};

и, наконец, у меня есть функция, которая выводит SignalCollection тип для некоторых Signal

template<class T>
auto& get_collection_for_signal(T&& t)
{
    return SignalCollection<T>::Instance();
}

Проблема в том, что я не могу добавить значения на карту коллекции. Вот главное:

void foo()
{

}
void main()
{
    Signal<void()> sgl = Signal<void()>{}; //Create signal
    sgl.slot = foo;                       //add slot

    auto& t = get_collection_for_signal(sgl); //Get collection for this signal which is SignalCollection<Signal<void()>>

    t.add(sgl); //error 1
    t.signals_map["a"] = sgl; //error 2
}

Ответы [ 3 ]

6 голосов
/ 13 марта 2019

Проблема здесь

template<class T>
auto& get_collection_for_signal(T&& t)
{
    return SignalCollection<T>::Instance();
}

Когда вы называете это как

auto& t = get_collection_for_signal(sgl);

T выводится как Signal<void()>&, и это означает, что вы возвращаете SignalCollection<Signal<void()>&>, что не то, что вы хотите. Что вам нужно сделать, это удалить ссылку из типа. Вы можете сделать это, используя

template<class T>
auto& get_collection_for_signal(T&&)
{
    return SignalCollection<std::remove_cv_t<std::remove_reference_t<T>>>::Instance();
}

, который удалил квалификацию cv и ссылку из T (C ++ 20 дает нам std::remove_cvref, так что это может быть сделано с одним помощником).

Вы также можете получить то же поведение с

template<class T>
auto& get_collection_for_signal(T)
{
    return SignalCollection<T>::Instance();
}

Это включает в себя намного меньше ввода и дает вам такое же поведение, так как квалификация высшего уровня cv исключается, и это никогда не выведет ссылку.


У вас также есть проблема с функцией add.

void add(T&& signal)
{
    this->signals_map.insert(std::make_pair("a", std::forward<T>(signal)));
}

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

template<typename U>
void add(U&& signal)
{
    this->signals_map.insert(std::make_pair("a", std::forward<U>(signal)));
}

наконец

void main()

это всегда неправильно. main() уполномочен вернуть int. Прочитайте Что должна вернуть main () в C и C ++? для получения дополнительной информации.

2 голосов
/ 13 марта 2019

Другие ответы хороши при объяснении ошибки, но я бы порекомендовал другое решение, чтобы устранить проблему.

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

template<class T>
auto& get_collection_for_signal(T const& t)
{
    return SignalCollection<T>::Instance();
}
2 голосов
/ 13 марта 2019

T&& не является ссылкой для пересылки, если T не выводится, и наоборот.

template<class T>
auto& get_collection_for_signal(T&& t)

T выводится как ссылка на Signal<void()> в вашем примере использования:

auto& t = get_collection_for_signal(sgl);

и возвращает:

return SignalCollection<T>::Instance();

, что:

SignalCollection<Signal<void()>&>::Instance()

чушь.

Почистить ссылки на пересылку и ссылки на l / rvalue. Каждое использование этого в вашем коде неправильно.

При проектировании у вас есть бесполезные типы - Signal<T> как написано, это стандартная функция с багажом, который ничего не делает - глобальные одиночные вызовы и дедукция типов, создающая глобальное состояние.

Я понимаю, что вы пытаетесь сделать, но это все равно, что делать пожарную машину из соломы.

...