C++ Передача реализации шаблонного интерфейса в качестве параметра

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

Чтобы проиллюстрировать проблему, я создал минимальный пример. См. produce(ISource<IProduct>* source) ниже:

// Product
class IProduct {
public:
    virtual ~IProduct() = default;
};
class Bread : public IProduct {};
class Cucumber : public IProduct {};

// Source
template<typename T>
class ISource {
public:
    virtual T* get() = 0;
};

class Bakery : public ISource<Bread> {
public:
    Bread* get() override {
        return new Bread();
    }
};

class Grocery : public ISource<Cucumber> {
public:
    Cucumber* get() override {
        return new Cucumber();
    }
};

// Producer
class IProducer {
public:
    virtual IProduct* produce(ISource<IProduct>* source) = 0;
};

class Shop : public IProducer {
public:
    IProduct* produce(ISource<IProduct>* source) override {
        return source->get();
    }
};

Мои попытки:

template<typename T>
T* test(IProducer* producer, ISource<T>* source) {
    // (1) Cannot initialize a parameter of type 'ISource<IProduct> *' with an lvalue of type 'ISource<Bread> *'
    T* product = dynamic_cast<T *>(producer->produce(source));
    return product;
}

int main(int argc, char** argv) {

    auto bakery = new Bakery();
    auto shop = new Shop();

    // FAILS: see (1)
    Bread* bread1 = test(shop, bakery);
}

Вы спрашиваете, почему ISource<Bread> не конвертируется неявно в ISource<IProduct>? Вы спрашиваете, почему функции-члены шаблона не могут быть виртуальными?

Drew Dormann 29.07.2024 21:01
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
83
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ISource<IProduct> и ISource<Bread> — это два совершенно отдельных и уникальных типа, их нельзя привести друг к другу, и вы не можете передать один там, где ожидается другой. (если вы сами не определите преобразование).

однако вы можете стереть их разные типы, используя стирание типов, и сохранить только тот факт, что они оба могут производить IProduct*

способ сделать это в C++ с использованием стандартной библиотеки — использовать std::function

#include <functional>

// Product
class IProduct {
public:
    virtual ~IProduct() = default;
};
class Bread : public IProduct {};

// Source
template<typename T>
class ISource {
public:
    virtual T* get() = 0;
};

class Bakery : public ISource<Bread> {
public:
    Bread* get() override {
        return new Bread();
    }
};

// Producer
class IProducer {
public:
    virtual IProduct* produce(std::function<IProduct*()> source) = 0;
};

class Shop : public IProducer {
public:
    IProduct* produce(std::function<IProduct*()> source) override {
        return source();
    }
};

template<typename T>
T* test(IProducer* producer, ISource<T>* source) {
    T* product = dynamic_cast<T*>(producer->produce([&]()->IProduct* {return source->get(); }));
    return product;
}

int main(int argc, char** argv) {

    auto bakery = new Bakery();
    auto shop = new Shop();

    Bread* bread1 = test(shop, bakery);
}

Помимо стирания типов, вы можете ISource не использовать шаблоны и использовать ковариантные возвращаемые типы.

class ISource {
public:
    virtual IProduct* get() = 0;
};

class Bakery : public ISource {
public:
    Bread* get() override {
        return new Bread();
    }
};

class Grocery : public ISource {
public:
    Cucumber* get() override {
        return new Cucumber();
    }
};

// Producer
class IProducer {
public:
    virtual IProduct* produce(ISource* source) = 0;
};

class Shop : public IProducer {
public:
    IProduct* produce(ISource* source) override {
        return source->get();
    }
};

template<typename source_t>
auto test(IProducer* producer, source_t* source) {
    auto product = dynamic_cast<decltype(source->get())>(producer->produce(source));
    return product;
}

Другие вопросы по теме