Вызов соответствующего метода для вектора производных классов - PullRequest
0 голосов
/ 26 мая 2018

У меня есть карта ключа и std :: vector и следующий код.

std::map<Key, std::vector<std::unique_ptr<Foo>>> myMap;

class Foo
{
    // removed for brevity
};

class Boo : public Foo
{
    // removed for brevity
};

class Doo : public Foo
{
    // removed for brevity
};

void doSomething(std::vector<std:unique_ptr<Boo>>& booList)
{
    // removed for brevity
}

void doSomething(std::vector<std:unique_ptr<Doo>>& dooList)
{
    // removed for brevity
}

И повторяюсь следующим образом:

for(auto& item : myMap)
{
    // removed for brevity
    doSomething(it.second);
}

Итак, во время итерации по карте я хочувызвать соответствующую функцию для каждого элемента карты на основе его производного типа класса, но получил следующую ошибку:

non-const lvalue reference to type 'vector<unique_ptr<Boo, default_delete<Boo>>, allocator<unique_ptr<Boo, default_delete<Boo>>>>' cannot bind to a value of unrelated type 'vector<unique_ptr<Foo, default_delete<Foo>>, allocator<unique_ptr<Foo, default_delete<Foo>>>>'

Есть ли какой-нибудь возможный способ достичь этого без приведения?

Ответы [ 4 ]

0 голосов
/ 26 мая 2018

Если вы не можете изменить реализацию Foo, Boo, Doo, то вы можете определить функции, не являющиеся членами, перегруженными, но которые не принимают вектор в качестве аргумента, а элемент вектора, потому что вы не можете передать векторк функции, ожидающей вектордаже если B является производным классом A. Тогда, надеясь, что Foo полиморфна, вы можете выбрать правильную функцию при помощи dynamic_cast.

#include <iostream>
#include <map>
#include <memory>
#include <vector>

using namespace std;

class Foo
{
    virtual void dummy_function_used_to_have_polymorphism() {}
};

class Boo : public Foo
{
};

class Doo : public Foo
{
};

void doSomething(Boo& boo)
{
    cout << "Boo do something" << endl;
}

void doSomething(Doo& doo)
{
    cout << "Doo do something" << endl;
}

using Key = int;
map<Key, vector<unique_ptr<Foo>>> myMap;

int main()
{
    myMap[10].push_back(make_unique<Boo>());
    myMap[10].push_back(make_unique<Doo>());

    myMap[1000].push_back(make_unique<Doo>());
    myMap[1000].push_back(make_unique<Boo>());

    for (auto& kv: myMap) {
        cout << "key: " << kv.first << endl;
        for (auto& foo: kv.second) {
            if (auto p = dynamic_cast<Boo*>(foo.get())) {
                doSomething(*p);
            } else
            if (auto p = dynamic_cast<Doo*>(foo.get())) {
                doSomething(*p);
            }
        }
    }
}
0 голосов
/ 26 мая 2018

Обычно вы пишете:

void callByFoo(Foo* foo) {
}

int main() {
    Boo* bp = new Boo();
    callByFoo(bp);
}

Но при использовании умного ptr вы инициализируете ссылки, поэтому у вас есть ссылки на различные типы, поэтому это не работает:

void callByFoo(std::unique_ptr<Foo>& fooRef) {
}

int main() {
    std::unique_ptr<Boo> bp = std::make_unique<Boo>();
    callByFoo(bp);
}

Iпредположим, что сравнение с первым примером выглядит так:

void callByFoo(Foo*& fooRef) {
}

int main() {
    Boo* bp = new Boo();
    callByFoo(bp);
}

Поэтому, чтобы заставить его работать, используйте базовый указатель, и типы будут совпадать, как в этом примере:

void callByFoo(std::unique_ptr<Foo>& fooRef) {
}

int main() {
    std::unique_ptr<Foo> bp = std::make_unique<Boo>();
    callByFoo(bp);
}

В вашемНапример, вам, очевидно, придется изменить методы на виртуальные, и сигнатура функции будет выглядеть следующим образом:

void doSomething(std::vector<std:unique_ptr<Foo>>& booList)
{
    // removed for brevity
} 
0 голосов
/ 26 мая 2018

Вы должны иметь виртуальный метод и выполнять итерации для каждого элемента вектора.

Пример ниже на C ++ 14:

#include <iostream>
#include <map>
#include <memory>
#include <vector>

using namespace std;

class Foo
{
  public:
    virtual void doSomething() = 0;
};

class Boo : public Foo
{
  public:
    virtual void doSomething() override
    {
        cout << "Boo do something" << endl;
    }
};

class Doo : public Foo
{
  public:
    virtual void doSomething() override
    {
        cout << "Doo do something" << endl;
    }
};

using Key = int;
map<Key, vector<unique_ptr<Foo>>> myMap;

int main()
{
    myMap[10].push_back(make_unique<Boo>());
    myMap[10].push_back(make_unique<Doo>());

    myMap[1000].push_back(make_unique<Doo>());
    myMap[1000].push_back(make_unique<Boo>());

    for (auto& kv: myMap) {
        cout << "key: " << kv.first << endl;
        for (auto& foo: kv.second) {
            foo->doSomething();
        }
    }
}
0 голосов
/ 26 мая 2018

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

template <class T>
class Foo
{
    virtual void doSomething(std::vector<std:unique_ptr<T>>& FooList);
};

class Boo : public Foo<Boo>
{
    virtual void doSomething(std::vector<std:unique_ptr<Boo>>& BooList) override;
};

class Doo : public Foo<Doo>
{
    virtual void doSomething(std::vector<std:unique_ptr<Doo>>& DooList) override;
};
...