С++ 11: расширение std:: is_pointer для std:: shared_ptr

Я думаю о перегрузке std::is_pointer в С++ 11, чтобы получить значение true для std::shared_ptr<T>, так как последнее ведет себя очень сильно, как T*.

#include <type_traits>

namespace std {

template <typename T> struct is_pointer<shared_ptr<T>> : std::true_type {};
template <typename T> struct is_pointer<shared_ptr<T const>> : std::true_type {};

}

Интересно, почему эта перегрузка еще не включена в стандартную реализацию. Есть ли какая-то ошибка, которую я не замечаю?

В качестве альтернативы можно было бы, конечно, ввести новый признак is_shared_ptr<T>.

На самом деле, я попробовал следующий код:

template <typename T> 
struct is_pointer<shared_ptr<typename std::remove_cv<T>::type>>
  : std::true_type 
{};

который не компилируется с помощью GCC 4.7 из-за

error: template parameters not used in partial specialization:
error:         ‘T’

Ответ 1

std::is_pointer как-то происходит от Boost и изначально предназначено для обнаружения указателей на raw указателей и указателей функций, вот нижняя нота, которую вы можете найти в Подготовить документацию:

is_pointer обнаруживает только "реальные" типы указателей, а не интеллектуальные указатели. Пользователи не должны специализировать is_pointer для типов интеллектуальных указателей, так как это может привести к тому, что код Boost (и другой сторонний) не будет работать правильно. Пользователи, которым нужна черта для обнаружения интеллектуальных указателей, должны создавать свои собственные. Однако обратите внимание, что вообще нет способа автоматического распознавания интеллектуальных типов указателей, поэтому такой признак должен быть частично специализированным для каждого поддерживаемого типа интеллектуального указателя.

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

В принципе, то, что вы ищете, было бы признаком, который будет искать типы, которые реализуют концепцию Dereferenceable (даже если это будет работать и для std::optional, а не только указателей/умных указателей).

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

Ответ 2

Я думаю о перегрузке std:: is_pointer в С++ 11, чтобы дать true для std:: shared_ptr, так как последнее ведет себя очень сильно, как T *.

Это не так. Удачи, сделав ++p или p[i] с помощью shared_ptr.

Интересно, почему эта перегрузка еще не включена в стандартную реализацию. Есть ли какая-то ошибка, которую я не замечаю?

Он не был включен, потому что это было бы просто неправильно: is_pointer сообщает вам, является ли тип типом указателя (§3.9.2). shared_ptr не является типом указателя, поэтому is_pointer<shared_ptr<int>::value be true будет просто ошибочным.

На самом деле, я попробовал следующий код:

template <typename T> 
struct is_pointer<shared_ptr<typename std::remove_cv<T>::type>>
  : std::true_type 
{};

Что, черт возьми, там делает remove_cv? Следующие "работы" в GCC.

template <typename T> 
struct is_pointer<shared_ptr<T>>
  : std::true_type 
{};

Однако он имеет поведение undefined. В общем, вам не разрешено добавлять специализации к шаблонам в пространстве имен std, если они не согласуются с определенной семантикой. Семантика для std::is_pointer<T> такова, что она выводится только из std::true_type, если T - тип указателя, который не является std::shared_ptr. Одного этого было бы достаточно, но в этом конкретном случае стандарт фактически идет на длину, запрещающую его явно (§20.9.2):

Поведение программы, которая добавляет специализации для любого из шаблонов классов, определенных в этом подпункте, это undefined, если не указано иное.

Единственным шаблоном из <type_traits>, который пользователь может добавить специализациям, является std::common_type.

Ответ 3

Хотя я согласен с тем, что более общие черты типа, такие как behaves_like_pointer (извините глупое имя), is_callable (для всех вещей, имеющих ()) или is_indexable (для подобных массиву вещей) были бы очень полезны, это, конечно, не то, для чего были созданы такие вещи, как is_pointer, is_function или is_array. Это черты, идентифицирующие актуальные типы типов жесткого языка, такие же, как is_integral, is_class или is_rvalue_reference.

Таким образом, overlaoding is_pointer для любого типа умного указателя будет соглашаться с этой первоначальной целью, и эта цель является четкой и недвусмысленной. Но я до сих пор согласен, что также будут полезны дополнительные более общие концептуальные типы типов, такие как is_smart_pointer, is_callable, is_nothrow_swappable или is_hashable.

Ответ 4

Другие ответы охватывали технические детали и часть фона.

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

То, что вы действительно хотите (IMO), не отвлекает шаблон is_pointer, а имеет функцию запроса, которая скрывает вашу семантику. Вот что вы должны делать: ваш собственный is_maybesmart_pointer < > , который сообщает true для оригинальных указателей и всего, что вы желаете. И используйте это в своем коде.

Тонкая настройка оригинала по оригинальной идее вполне могла бы привести к нарушению ODR, как вы узнали, что лики, которые вы связываете, еще не использовали is_pointer с shared_ptr или что-то еще?