Можно ли создать метод, который принимает конкретную реализацию шаблонного интерфейса?
Чтобы проиллюстрировать проблему, я создал минимальный пример. См. 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<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;
}
Вы спрашиваете, почему
ISource<Bread>
не конвертируется неявно вISource<IProduct>
? Вы спрашиваете, почему функции-члены шаблона не могут быть виртуальными?