на умных указателях вывод / замена шаблона не удалась - PullRequest
4 голосов
/ 29 апреля 2020

Давайте рассмотрим этот код:

template<typename T>
struct A
{
//...
};

struct B : public A<int>
{
//...
};

template<typename T>
bool validate(A<T>* p)
{
    //...
    return true;
};

int main()
{
  A<int>* pA;
  std::cout << validate(pA) << std::endl;

  B* pB;
  std::cout << validate(pB) << std::endl;
}

Он компилируется правильно и работает как положено. Теперь, скажем, мне нужно реорганизовать код, чтобы вместо него использовать умные указатели, затем также можно изменить validate следующим образом:

template<typename T>
bool validate(std::shared_ptr<A<T>> p)
{
    //...
    return true;
};

int main()
{
  std::shared_ptr<A<int>> pA = std::make_shared<A<int>>();
  validate(pA);  //it compiles correctly

  std::shared_ptr<B> pB = std::make_shared<B>();
  validate(pB);  //it FAILS to compile
}

Вы можете проверить, что здесь .

В чем причина этого?

Как лучше всего решить эту проблему без изменения A или B?

Ответы [ 3 ]

1 голос
/ 29 апреля 2020

Это потому, что для устранения неоднозначности параметров функции шаблона требуется выполнить пользовательское приведение от shared_ptr<B> до shared_ptr<A<int>>. Устранение неоднозначности параметров шаблонных функций даже не пытается выполнять приведение типов (кроме некоторого базового c материала).

Это даже не практично, даже пытаться. Ну, теоретически может быть частичное решение, которое указывает, какие кастинги попробовать, но это не так. Просто используйте SFINEA и устраните неоднозначность самостоятельно, вместо того чтобы просить компилятор сделать это за вас.

0 голосов
/ 29 апреля 2020

В общем, вам следует избегать умных указателей, если вызываемая функция не меняет владельца! Используйте свою функцию необработанного указателя.

Вы используете тип A Generi c в функции validate. Наследование здесь не рассматривается.

Если вы игнорируете наследование, оно может выглядеть следующим образом:

template<typename T>
bool validate(std::shared_ptr<T> p)
{
    return true;
}

См. Godbolt

Для принудительного использования базового класса Я хотел бы ввести Typetag.

0 голосов
/ 29 апреля 2020

Возможные пути решения этой проблемы:

Альтернатива 1)

  std::shared_ptr<B> pB = std::make_shared<B>();
  //... do your type B related operations through pB
  validate(std::shared_ptr<A<int>>(pB));

Альтернатива 2)

template<typename T>
bool validate(A<T> const & a)
{
    //...
    return true;
}

int main()
{
  std::shared_ptr<A<int>> pA = std::make_shared<A<int>>();
  validate(*pA);

  std::shared_ptr<B> pB = std::make_shared<B>();
  validate(*pB);
}

Альтернатива 3)

template<typename T>
bool validate(std::shared_ptr<A<T>> p)
{
    //...
    return true;
}

template<typename T>
bool validate(std::shared_ptr<T> p)
{
    //...
    return true;
}


int main()
{
  std::shared_ptr<A<int>> pA = std::make_shared<A<int>>();
  validate(pA); // it uses bool validate(std::shared_ptr<A<T>> p)

  std::shared_ptr<B> pB = std::make_shared<B>();
  validate(pB); // it uses bool validate(std::shared_ptr<T> p)
}

но это подразумевает, что функция может распространяться на многие другие типы, и это не обязательно желаемое поведение (может быть?).

Любые другие предложения, ребята? Было бы здорово, если бы кто-то знал, почему код в вопросе не работает с самого начала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...