Пропагат_конст и прямое объявление

Я только что столкнулся с любопытной ошибкой std:: эксперимент::а pateate_const. Следующий фрагмент демонстрирует проблему

#include <memory>
#include <experimental/propagate_const>
#include <map>

class FWD;

//compiles
class A
{
    std::unique_ptr<FWD> m;
};

//compiles
class B
{
    std::experimental::propagate_const<std::unique_ptr<FWD>> m;
};

//compiles
class C
{
    std::unique_ptr<std::map<int, FWD>> m;
};

//does not compile!
class D
{
    std::experimental::propagate_const<std::unique_ptr<std::map<int, FWD>>> m;
};

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

Я был бы признателен, если бы кто-нибудь объяснил мне, почему компиляция не удалась в текущей реализации пропагата_конст. Это как-то связано с

typedef remove_reference_t<decltype(*std::declval<_Tp&>())> element_type;

Потому что это обходной путь:

template <typename T, typename = void>
struct get_element_type
{
  using type = std::remove_reference_t<decltype(*std::declval<T&>())>;
};

template <typename T>
struct get_element_type<T, typename std::enable_if<!std::is_void<typename T::element_type>::value>::type>
{
  using type = typename T::element_type;
};

// Namespaces and class declaration...

using element_type = typename get_element_type<T>::type;

Проверенные компиляторы: clang, gcc.

PS Интересно, знают ли разработчики компилятора об этом или нет.

1 ответ

Решение
  1. Создание шаблона стандартной библиотеки с неполным типом обычно запрещено.

  2. std::map не является исключением из этого правила.

  3. Запрос decltype(*std::declval<_Tp&>()) с _Tp = std::unique_ptr<std::map<int, FWD>> требует создания экземпляров всех связанных классов _Tp искать потенциального друга operator* деклараций.

  4. Среди этих ассоциированных классов std::map<int, FWD>,

  5. Создание std::map<int, FWD> вызывает неопределенное поведение.

Я отправил патч для duplicate_const, который заменяет существующее решение и решает проблему, как предложенное выше решение, но не зависит от SFINAE:

template<class U>
struct detect_element_type {
    using type = typename U::element_type;
};

template<class U>
struct detect_element_type<U*> {
    using type = U;
};

using element_type = typename detect_element_type<T>::type;
Другие вопросы по тегам