Ошибка с вычитанием аргумента std:: shared_ptr, наследования и шаблона
Я пытаюсь использовать вывод аргумента шаблона с наследованием и std::shared_ptr
. Как вы можете видеть в приведенном ниже примере кода, я передаю shared_ptr<Derived>
в шаблонную функцию, отличную от члена, которая должна делать вывод аргумента шаблона. Если я вручную назову тип, все будет работать, и если я позволю ему сделать вывод аргумента шаблона, это не так. Казалось бы, что компилятор не смог определить тип, однако сообщение об ошибке показывает, что это произошло. Я не уверен, что происходит здесь, и я был бы признателен за любой вклад. (Visual Studio 2010)
#include <memory>
template <typename T>
class Base {};
class Derived : public Base<int> {};
template <typename T>
void func(std::shared_ptr<Base<T> > ptr) {};
int main(int argc, char* argv[])
{
std::shared_ptr<Base<int>> myfoo(std::shared_ptr<Derived>(new Derived)); // Compiles
func(myfoo); // Compiles
func<int>(std::shared_ptr<Derived>(new Derived)); // Compiles
func(std::shared_ptr<Derived>(new Derived)); // Doesn't compile. The error message suggests it did deduce the template argument.
return 0;
}
Сообщение об ошибке:
5> error C2664: 'func' : cannot convert parameter 1 from 'std::tr1::shared_ptr<_Ty>' to 'std::tr1::shared_ptr<_Ty>'
5> with
5> [
5> _Ty=Derived
5> ]
5> and
5> [
5> _Ty=Base<int>
5> ]
5> Binding to reference
5> followed by
5> Call to constructor 'std::tr1::shared_ptr<_Ty>::shared_ptr<Derived>(std::tr1::shared_ptr<Derived> &&,void **)'
5> with
5> [
5> _Ty=Base<int>
5> ]
5> c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\memory(1532) : see declaration of 'std::tr1::shared_ptr<_Ty>::shared_ptr'
5> with
5> [
5> _Ty=Base<int>
5> ]
5>
Ответы
Ответ 1
В то время как компилятор может выполнять преобразования на основе базы при выполнении вывода типа, std::shared_ptr<Derived>
сам по себе не вытекает из std::shared_ptr<Base<int>>
.
Существует определенное пользователем преобразование между двумя, которое позволяет shared_ptr
вести себя как обычные указатели относительно полиморфизма, но компилятор не будет принимать во внимание определяемые пользователем преобразования при выполнении вывода типа.
Без учета пользовательских преобразований компилятор не может вывести T
, который сделает shared_ptr<Base<T>>
либо идентичным, либо shared_ptr<Derived>
, либо базовый класс shared_ptr<Derived>
(еще раз shared_ptr<Base<int>>
не является базовый класс shared_ptr<Derived>
).
Поэтому вывод типа не выполняется.
Чтобы обойти эту проблему, вы можете позволить параметру вашей функции быть простым shared_ptr<T>
и добавить ограничение SFINAE, которое обеспечило бы, чтобы ваша перегрузка была выбрана, только когда тип аргумента получен из (или ) экземпляр шаблона класса Base
:
#include <type_traits>
namespace detail
{
template<typename T>
void deducer(Base<T>);
bool deducer(...);
}
template <typename T, typename std::enable_if<
std::is_same<
decltype(detail::deducer(std::declval<T>())),
void
>::value>::type* = nullptr>
void func(std::shared_ptr<T> ptr)
{
// ...
}
Вот живой пример.
Ответ 2
Он работает, если вы пишете его следующим образом:
template <typename T>
void func(std::shared_ptr<T> ptr) {};
Если вы действительно хотите явно заблокировать функцию от вызова с чем-то, не полученным из Base, вы можете использовать type_traits/enable_if/etc.