enable_if в конструкторе преобразования (статическое приведение, is_base_of)

Я работаю над реализацией общего указателя. (используя С++ 17, если это имеет значение)

Единственная проблема - конструктор преобразования. Я хочу иметь возможность статического преобразования smart_ptr в smart_ptr базового типа.

template<typename U>
inline smart_ptr(const smart_ptr<U>& rhs)
{
    ...
}

Это работает, но он также попытается преобразовать smart_ptr в smart_ptr любого другого типа. Например, если у меня есть перегруженная функция, которая может принимать различные виды smart_ptr несвязанного типа, я получаю ошибку компилятора о неоднозначной перегрузке. Итак, мне нужно преобразование из smart_ptr -> smart_ptr только в том случае, если U является производным классом от T.

Похоже, это должно сработать. Он компилируется, но делает наоборот. Это предотвращает работу действительных статических восходящих преобразований, но все же позволяет выполнять приведение к несвязанным типам:

template<typename U>
inline local_shared_ptr(typename enable_if<is_base_of<T,U>::value, const  local_shared_ptr<U>&>::type rhs)
{
    ...
}

РЕДАКТИРОВАТЬ:

Получилось, спасибо за помощь. Я выбираю решение Джарода, так как считаю template <typename U, enable_if_t<is_base_of<T,U>::value, int> = 0> наиболее лаконичным. Я не знал, что SFINAE может быть таким лаконичным.

Кроме того, поскольку это было упомянуто Натаном:

Как ни странно, одна из проблем, с которыми я столкнулся, заключается в том, что я ожидал вызова конструктора копирования шаблона, когда правая часть имеет тот же тип. По-видимому, компилятор не считает это реализацией конструктора копирования, и вместо этого вызывается автоматически сгенерированный конструктор копирования. Та же проблема для конструктора перемещения и оператора =. Не уверен, что это ошибка MSVC2019.


person stands2reason    schedule 17.02.2020    source источник


Ответы (2)


U нельзя вывести с помощью

template<typename U>
local_shared_ptr(enable_if_t<is_base_of<T,U>::value, const  local_shared_ptr<U>&> rhs)
{
// ...
}

А поскольку это конструктор, вы даже не можете указать шаблон явно. Так что этот конструктор бесполезен.

Вместо этого вы можете использовать:

  • Параметр по умолчанию (наиболее похожий на вашу попытку IMO):

    template <typename U>
    local_shared_ptr(const local_shared_ptr<U>& rhs, enable_if_t<is_base_of<T,U>::value, int> = 0)
    {
    // ...
    }
    
  • параметр шаблона по умолчанию (предпочтительный способ):

    template <typename U, enable_if_t<is_base_of<T,U>::value, int> = 0>
    local_shared_ptr(const local_shared_ptr<U>& rhs)
    {
    // ...
    }
    

И поскольку вы используете конструктор, вы не можете использовать возвращаемое значение.

person Jarod42    schedule 17.02.2020

Поместите enable_if в список параметров шаблона, например

template<typename U, std::enable_if_t<std::is_base_of_v<T, U>, bool> = true>
inline smart_ptr(const smart_ptr<U>& rhs)
{

}

И теперь это будет вызываться только в том случае, если U равно T или получено из T. Если вы не хотите использовать это, если U == T, вы можете использовать

template<typename U, std::enable_if_t<std::is_base_of_v<T, U> && !std::is_same_v<T, U>, bool> = true>
inline smart_ptr(const smart_ptr<U>& rhs)
{

}
person NathanOliver    schedule 17.02.2020