Создание экземпляра шаблона C ++ с shared_ptr для const T - PullRequest
4 голосов
/ 08 апреля 2011

Предположим, у меня есть класс

template <typename T>
class A {
 public:
  template <typename V>
    void f(std::tr1::shared_ptr<const std::vector<V> > v1, 
           std::tr1::shared_ptr<const std::vector<float> > v2) {}
};

Следующее не компилируется:

 A<string> a;
  std::tr1::shared_ptr<std::vector<float> > v1(new std::vector<float>());
  std::tr1::shared_ptr<std::vector<float> > v2(new std::vector<float>());
  a.f(v1, v2);

Ошибка компилятора:

error: no matching function for call to 'A<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::f(std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&, std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&)'

Компилятору не удалосьсделать std::tr1::shared_ptr<std::vector<float> > в std::tr1::shared_ptr<const std::vector<float> > для первого аргумента.И все же это может быть для второго (не шаблонный аргумент).

Одним из решений этого является изменение вызова функции f (), вызывая его следующим образом f<float>(...).
Другое решение - объявление v1от shared_ptr до const vector<float>.

  1. Почему создание экземпляра шаблона здесь ведет себя так по-разному?
  2. Мое понимание наличия shared_ptr<const T> в качестве аргумента для метода заключается в том, что метод не может изменить то, чтоshared_ptr указывает на.Если мы изменим shared_ptr s на необработанные указатели и v1, v2 в качестве необработанных указателей на vectors, то код скомпилируется нормально.Что такое shared_ptr с, что нарушает вывод шаблона?

Ответы [ 2 ]

4 голосов
/ 15 декабря 2011

Связанные типы?

Как вы знаете, T* и const T* являются связанными типами.Существует стандартное преобразование из первого во второе (квалификационное преобразование).

Во-первых, вы должны понимать, что A<T> и A<const T> не относятся к общим типам.Эти типы могут иметь разный размер, представление и назначение.Один может быть определен, но не другой.

В частности, A<const T> не является константной квалификацией A<T>, поэтому между ними нет квалификационного преобразования, и C ++ недостаточно гибок, чтобы объявить пользователяопределяемые квалификации-конверсии.(Пользователь не может объявить любое стандартное преобразование, только пользовательское преобразование - пользовательское преобразование не является стандартным преобразованием.)

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

shared_ptr<>

shared_ptr<> предназначен для формирования семейства совместимых типов: shared_ptr<T> неявно преобразуется в shared_ptr<U> тогда T* неявно конвертируется в U*.В частности, shared_ptr<T> неявно преобразуется в shared_ptr<const T>.Он также неявно преобразуется в shared_ptr<void>, по той же причине.

Поскольку типы shared_ptr<const T> и shared_ptr<T> не связаны каким-либо особым образом, преобразование из shared_ptr<T> в shared_ptr<const T> не являетсяотличается от shared_ptr<T> до shared_ptr<void>.Это всего лишь два разных преобразования, но ни одно из них не может считаться «предпочтительным» в любом контексте, в отличие от преобразования из T* в const T* (rank = Exact match), которое предпочтительнее, чем преобразование из T* в void* (rank = Conversion).

Шаблоны функций

Некоторые стандартные преобразования разрешены для аргументов функции выведенных шаблонов:

  • квалификационных преобразований
  • некоторых преобразователей указателей: производное-к-основанию
  • ...

Но, как мы видели, между shared_ptr<> типами таких преобразований не существует.

Это означает, что дажеесли компилятору было разрешено перечислять все возможные типы для параметра шаблона, чтобы превратить шаблон функции

template <typename V>
void f (shared_ptr<const T> v1);

в бесконечный набор прототипов функций:

for every type T,
such that shared_ptr<const T> can be instantiated:
f (shared_ptr<const T>)

, если у вас нет точногосоответствовать, вы не сможете вызвать функцию: учитывая объявления

struct Base {};
struct Derived : Base {};

shared_ptr<Derived> d;

среди множества f прототипов, есть:

f (shared_ptr<const Base>)
f (shared_ptr<const Derived>)

, поэтому вызов f (d) шможет быть неоднозначным, поскольку оба этих кандидата включают в себя различные пользовательские преобразования.

3 голосов
/ 09 апреля 2011

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

A shared_ptr<T> неявно преобразуется в shared_ptr<const T> через конструктор преобразования. К сожалению, такие преобразования не могут использоваться при выводе аргументов шаблона.

Чтобы вывод аргумента был успешным, тип аргумента должен точно соответствовать типу параметра; большинство преобразований, включая пользовательские преобразования, не используются.

Допускается только несколько очень простых преобразований, включая затухание массива в указатель и преобразование функции в указатель функции (поскольку ни один из параметров функции не может иметь тип массива или тип функции, эти преобразования не могут привести к путанице). Кроме того, квалификаторы const- и volatile- верхнего уровня полностью игнорируются.

...