SFINAE внутри аргумента std::enable_if

У меня есть разные типы view, каждый из которых имеет константу-член std::size_t View::dimension и тип члена typename View::value_type.

Следующая проверка типа компиляции должна проверить, являются ли From и To представлениями (проверено с помощью is_view<>), а содержимое From может быть назначено To. (те же размеры и типы конвертируемых значений).

template<typename From, typename To>
struct is_compatible_view : std::integral_constant<bool,
    is_view<From>::value &&
    is_view<To>::value &&
    From::dimension == To::dimension &&
    std::is_convertible<typename From::value_type, typename To::value_type>::value
> { };

is_view<T> таков, что он всегда оценивается как std::true_type или std::false_type для любого типа T. Проблема в том, что если From или To не является типом представления, то From::dimension (например) может не существовать, а is_compatible_view<From, To> вызывает ошибку компиляции. Вместо этого в этом случае он должен оцениваться как std::false_type.

is_compatible_view используется для SFINAE с std::enable_if для отключения функций-членов. Например, класс представления может иметь функцию-член

struct View {
    constexpr static std::size_t dimension = ...
    using value_type = ...

    template<typename Other_view>
    std::enable_if_t<is_compatible_view<Other_view, View>> assign_from(const Other_view&);

    void assign_from(const Not_a_view&);
};

Not_a_view не является представлением и вызывает ошибку компиляции в is_compatible_view<Not_a_view, ...>. При вызове view.assign_from(Not_a_view()) SFINAE не применяется, и вместо этого возникает ошибка компиляции, когда компилятор пытается разрешить первую функцию assign_from.

Как можно написать is_compatible_view, чтобы это работало правильно? В С++ 17 позволяет ли std::conjunction<...> это?


person tmlen    schedule 06.01.2017    source источник


Ответы (1)


Один из подходов заключается в использовании чего-то вроде std::conditional для отсрочки оценки некоторых частей вашего признака типа до тех пор, пока мы не проверим, что другие части вашего признака типа уже верны.

То есть:

// this one is only valid if From and To are views
template <class From, class To>
struct is_compatible_view_details : std::integral_constant<bool,
    From::dimension == To::dimension &&
    std::is_convertible<typename From::value_type, typename To::value_type>::value
> { };        

// this is the top level one
template<typename From, typename To>
struct is_compatible_view : std::conditional_t<
    is_view<From>::value && is_view<To>::value,
    is_compatible_view_details<From, To>,
    std::false_type>::type
{ };

Обратите внимание, что я использую как conditional_t, так и ::type. is_compatible_view_details будет создан только в том случае, если и From, и To являются представлениями.


Аналогичным подходом было бы использование std::conjunction с приведенным выше, что из-за короткого замыкания также задержит оценку:

template <class From, class To>
struct is_compatible_view : std::conjunction_t<
    is_view<From>,
    is_view<To>,
    is_compatible_view_details<From, To>
    >
{ };

В любом случае, вам нужно вытащить детали.


Третьим подходом было бы использование enable_if_t в качестве специализации:

template <class From, class To, class = void>
struct is_compatible_view : std::false_type { };

template <class From, class To>
struct is_compatible_view<From, To, std::enable_if_t<
    is_view<From>::value &&
    is_view<To>::value &&
    From::dimension == To::dimension &&
    std::is_convertible<typename From::value_type, typename To::value_type>::value>>
: std::true_type { };

Здесь, если какое-либо из выражений в enable_if_t неправильно сформировано, срабатывает SFINAE, и мы просто используем основной шаблон, которым является false_type.

person Barry    schedule 06.01.2017
comment
На самом деле я не вижу смысла в ::type в вашем первом фрагменте. Кроме того, в зависимости от варианта использования может быть достаточно создать шаблон псевдонима enable_if_compatible_view, который просто перенесет любые ошибки в From::value_type и т. д. в непосредственный контекст. - person T.C.; 07.01.2017