2015-03-23 3 views
1

Проблема:дублирования кода для функций, которые принимают shared_ptr и unique_ptr

Давайте предположим, что у меня есть алгоритм, который принимает unique_ptr к некоторому типу:

void FancyAlgo(unique_ptr<SomeType>& ptr); 

Теперь я shared_ptr sPtr к SomeType, и мне нужно применить тот же алгоритм на sPtr. Означает ли это, что я должен дублировать алгоритм только для shared_ptr?

void FancyAlgo(shared_ptr<SomeType>& sPtr); 

Я знаю, что интеллектуальные указатели поставляются с владением базовым управляемым объектом в куче. Здесь, в моем FancyAlgo, владение обычно не является проблемой. Я думал о отгонкой смарт слой указателя и сделать что-то вроде:

void FancyAlgo(SomeType& value); 

и когда мне нужно вызвать его с unique_ptr:

FancyAlgo(*ptr); 

также для shared_ptr.

1, Является ли это приемлемый стиль в производстве кода? (Я где-то видел, что в контексте интеллектуальных указателей, вы не должны манипулировать сырые указатели подобным образом. Она имеет опасность введения таинственных ошибок.)

2, Можете ли вы предложить лучший способ (без дублирования кода), если 1 не является хорошей идеей.

Спасибо.

+1

Умные указатели почти никогда не передаются по ссылке, если вы не хотите говорить о самом умном указателе, а не о точном. Используйте '.get()' или '*'. –

+0

Почему бы и нет? В старом C++ 03 было отлично, чтобы передать указатели. Теперь это просто указатели с владением. Я не понимаю, почему это влияет на ваше решение. – h9uest

+1

Потому что, как правило, указатель не является объектом интереса, но он имеет смысл. –

ответ

1

Умные указатели о собственности. При запросе умного указателя запрашивается информация о владельце или управление.

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

Просьба указать ссылку const lvalue на интеллектуальный указатель, запрашивая разрешение на запрос статуса владения этим значением.

Просить ссылку rvalue на смарт-указатель является «раковиной» и обещает отнять эту собственность от вызывающего.

Просьба указать ссылку const rvalue - плохая идея.

Если вы получаете доступ к значению , указанному в значении, и вы хотите, чтобы он был не нулевым, ссылка на базовый тип хороша.

Если вы хотите быть обнуляемыми, boost::optional<T&> или T* являются приемлемым, как это std::experimental «мир тупого смарт-указатель» (или эквивалент рукописного один). Все они не имеют ссылок на nullable для некоторой переменной.

В интерфейсе не просите о вещах, которые вам не нужны, и они не понадобятся в будущем. Это делает рассуждение о том, что функция делает сложнее, и приводит к таким проблемам, как у вас в OP. Функция, которая повторно запускает ссылку, является совсем другой функцией от той, которая считывает значение.


Теперь более интересный вопрос, основанный от вас один, где вы хотите функция пересаживаться смарт-указатель, но вы хотите быть в состоянии сделать это для обоих общих и уникальных входов указателя. Это своего рода странный случай, но я мог бы представить себе тип типа-стирания-вниз-на-emplace (emplace_sink<T>).

template<class T> 
using later_ctor = std::function<T*(void*)>; 

template<class T, class...Args> 
later_ctor<T> delayed_emplace(Args&&...args) { 
    // relies on C++1z lambda reference reference binding, write manually 
    // if that doesn't get in, or don't want to rely on it: 
    return [&](void* ptr)->T* { 
    return new T(ptr)(std::forward<Args>(args)); 
    }; 
} 

namespace details { 
    template<class T> 
    struct emplace_target { 
    virtual ~emplace_target() {} 
    virtual T* emplace(later_ctor<T> ctor) = 0; 
    }; 
} 
template<class T> 
struct emplacer { 
    std::unique_ptr<emplace_target<T>> pImpl; 
    template<class...Args> 
    T* emplace(Args&&... args) { 
    return pImpl->emplace(delayed_emplace<T>(std::forward<Args>(args)...)); 
    } 
    template<class D> 
    emplacer(std::shared_ptr<T, D>& target): 
    pImpl(new details::emplace_shared_ptr<T,D>(&target)) // TODO 
    {} 
    template<class D> 
    emplacer(std::unique_ptr<T, D>& target): 
    pImpl(new details::emplace_unique_ptr<T,D>(&target)) // TODO 
    {} 
}; 

и т. Д. Необходимо много лака. Идея состоит в том, чтобы нарисовать-стирать конструкцию объекта T в произвольном контексте. Возможно, нам понадобится специальный случай shared_ptr, поэтому мы можем позвонить по номеру make_shared, так как задерживаемый ctor void*->T* недостаточно хорош, чтобы вытащить это (не в основном, а из-за отсутствия крючков API).

Aha! Я могу сделать общий общий ptr без особого обложки.

Выделяет блок памяти (char[sizeof(T)]) с деструктором, который преобразует буфер T затем вызывает удаление, в месте построить в этом буфере (получение T*), а затем преобразовать в shared_ptr<T> через shared_ptr<T>(shared_ptr<char[sizeof(T)]>, T*) конструктора. При тщательном исключении ловли это должно быть безопасным, и мы можем использовать нашу функцию размещения в комбинированном буфере make_shared.

+0

Для первой части я оставил два комментария под OP для расширенного обсуждения двусмысленности собственности и потенциальной проблемы с двойным удалением, вызванной там. Для второй части, emplacer безумный, но мне это нравится: p Почему мне нужен emplacer, как это? Насколько я понимаю, вы пытаетесь реализовать механизм виртуального конструктора (и задали его как контекст для обсуждения shared_ptr/unique_ptr), но в какой ситуации это было бы необходимо и полезно? – h9uest

+0

@ h9uest Да, 'T *' имеет неоднозначное владение. Следовательно, [N3840] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3840.pdf), The Dumbest Smart Pointer - 'std :: experimental :: observer_ptr' - в котором говорится: «Я не владею тем, что я указываю», и в противном случае ведет себя так же, как «T *», насколько это возможно.«T &» - это непустое число, не подлежащее переустановке, «T *», которое обычно * не * принадлежит тому, на что оно ссылается, и которое имеет ссылочную семантику и не может иметь «свой» адрес, или его «биты», написанные (или действительно прочитанные) любым способом, совместимым со стандартами, тем самым упрощая некоторые оптимизации для компилятора. – Yakk

+0

@ h9uest, как и для второго, если вы хотите использовать функцию, которая генерирует 'unique_ptr ' или 'shared_ptr ', 'emplacer ' может стереть разницу. Тогда вам понадобится одна реализация, и эта реализация может быть внутри файла .cpp' вместо шаблона, открытого в заголовке. Альтернативой 'emplacer ' является 'std :: function ' - значение-приемник, но это требует, чтобы ваш 'T' был подвижным (идеально эффективным). Платформа - это, по сути, «более сильный» приемник, который поддерживает больше типов и более эффективен. вместо выходных итераторов можно использовать приемники/усилители ценности. – Yakk

Смежные вопросы