2016-12-01 16 views
0

Я читаю статью 28 по умным указателям Скотта Мейерса Более эффективный C++ и у вас есть следующий вопрос.Удалить неопределенность в вызове функции на неявно преобразованном умном указателе

Полная демонстрация можно найти по адресу: http://ideone.com/aKq6C0.

Производная указатель класса можно преобразовать неявно указатель базового класса:

class Base {}; 
class Derived : public Base {}; 
void foo(Base* b) { cout << "foo called on Base pointer" << endl;} 
Derived *d = new Derived(); 
foo(d); //No problem 

Но такое неявное преобразование не может произойти для смарт-указатели, т.е. SmartPtr<Derived> не может быть неявно преобразован в SmartPtr<Base>. Таким образом, мы используем шаблон члена для таких преобразований:

template<typename T> 
class SmartPtr { 
public: 
    //constructors, operator->, etc 

    //member template for type conversion 
    template<NewType> 
    operator SmartPtr<NewType>() { 
    return SmartPtr<NewType>(pointee); 
    } 
private: 
    T* pointee;//the raw pointer 
}; 

Это почти работает, но это может привести к двусмысленности:

class Remote {}; 
class Base : public Remote {}; 
class Derived : public Base {}; 
void foo(const SmartPtr<Remote>& p) { cout << "remote" << endl;} 
void foo(const SmartPtr<Base>& p) { cout << "base" << endl;} 

SmartPtr<Derived> d(new Derived()); 
foo(d);//compile error: ambiguity 

В этом примере компилятор не знает, должен ли он конвертировать d в SmartPtr<Base> или SmartPtr<Remote>, хотя для исходного указателя Base явно превосходит. В книге написано:

Лучшее, что мы можем сделать, это использовать шаблоны-члены для генерации функций преобразования, а затем использовать броски в тех случаях, когда результаты неоднозначности.

Но как именно мы применяем литье здесь? foo(static_cast<SmartPtr<Base>>(d)) не скомпилируется. Из сообщения об ошибке я могу сказать, что ошибка исходит из использования неконстантной ссылки в конструкторе-копии SmartPtr. Мне интересно, каков правильный способ вызова функции.

+0

не отвечает qustion, но 'вернуть SmartPtr (пуанты e); 'Кажется не правильным, вам нужно что-то вроде' std :: week_ptr', чтобы сделать это –

+0

Вы должны посмотреть на стандартные интеллектуальные указатели и как они обеспечивают преобразование (например) из 'std :: shared_ptr ' to 'std :: shared_ptr 'когда (и только тогда)' Derived * 'конвертируется в' Base * '. –

+0

Ваш трюк верен, отсутствует код для ваших конструкторов копий. Если у вас есть тот, который принимает non-const ref - вы получите ошибку, о которой вы упомянули, если вы можете просто изменить ref на const в copy ctor, а код будет компилироваться (проверен) –

ответ

0
//constructors 

Это самая важная часть, которую вы опустили :)

гипсом правильно, как таковой, все это зависит от множества конструкторов у вас есть. Если у вас есть тот, который принимает non-const ref - вы получите сообщение об ошибке, которое вы упомянули, если вы можете просто изменить ref на const в copy ctor и код будет компилироваться.

Это может быть не очень очевидно, но когда вы звоните

return SmartPtr<NewType>(pointee); 

вы строите новый SmartPtr<NewType>, которые должны принять T* и NewType и T различные типы. Скорее всего, у вас есть только ctor, который принимает необработанный указатель того же типа (т.T* для SmartPtr<T> и для SmartPtr<X>), поэтому компилятор ищет другой converting ctor создать новый SmartPtr и находит вашу копию CTOR, но он не может связать новое значение, не сопзЬ реф


Edit:

Если вы используете C++ 11 добавление перемещения CTOR также решить вашу проблему, как она будет иметь возможность связываться с RValue

SmartPtr(SmartPtr<T>&& other): pointee(other.pointee) { other.pointee = nullptr; } 
+0

Спасибо за помощь. Вы можете проверить полное определение «SmartPtr» на странице ideone, представленной выше http://ideone.com/aKq6C0. Я взял это непосредственно из книги, и поведение «SmartPtr» в основном решает, что в своем конструкторе копирования он должен взять неконстантную ссылку (вроде как устаревший «auto_ptr»). –

+0

Вы используете C++ 11? вы можете решить свою проблему, добавив move ctor? 'SmartPtr (SmartPtr &&)' .. –