Есть ли способ преобразовать ошибку замены шаблона в значение bool (true / false) или теги (std :: true_type / std :: false_type) - PullRequest
0 голосов
/ 21 декабря 2018

Я немного "играю" с SFINAE в стиле C ++ 17, и я пытаюсь добиться результата, в котором сбой замещения вместо того, чтобы просто заставить компилятор перейти к следующему случаю, вызывает "компилятор" (скорее класс, который подвергся такой попытке) сообщить (через false / std::false_type), что подстановка не удалась, и только тогда компилятор переходит к следующему случаю.

Мои текущие попытки сводятся к чему-то вродечто:

template <typename T>
class logic_value
{
    static constexpr bool result = std::is_same<
        std::enable_if_t<T, std::true_type>,
        std::true_type>;
};

template <typename T>
inline constexpr bool lv = logic_value<T>::result;

(пример использования:)

template <typename T>
std::enable_if_t<
    lv<decltype(std::declval<T>() + std::declval<T>())> /* has plus operator */ 
    && 
    (lv<decltype(std::declval<T>().func_foo())> || lv<decltype(std::declval<T>().func_bar())>) /*has either func_foo() or func_bar() */ 
    &&
    lv<T&> /* can have a reference */
    && 
    (!lv<decltype(std::declval<T>().func_foobar())>) /* does NOT have a func_foobar() */
, T> const & Ioo(T const &);

, но это не работает так, как я себе представлял ...: / Особенно сложный случай, когдаЯ использую оператор logic not (!), чтобы гарантировать, что что-то в тестируемом типе не существует.

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Способ сделать это с помощью идиомы обнаружения, вы хотите is_detected с соответствующими псевдонимами.

В вашем конкретном случае:

template <typename T> using has_plus_t = decltype(std::declval<T>() + std::declval<T>());
template <typename T> using func_foo_t = decltype(std::declval<T>().func_foo());
template <typename T> using func_bar_t = decltype(std::declval<T>().func_bar());
template <typename T> using func_foobar_t = decltype(std::declval<T>().func_foobar());

template <typename T>
enable_if_t<
    is_detected_v<has_plus_t, T>
    && 
    (is_detected_v<func_foo_t, T> || is_detected_v<func_bar_t, T>)
    &&
    is_detected_v<add_lvalue_reference_t, T>
    && 
    !is_detected_v<func_foobar_t, T>
, T> const & Ioo(T const &);

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

0 голосов
/ 21 декабря 2018

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

Я делаю так:

#include <utility>
#include <array>

template<typename T, typename U>
constexpr auto check_addition(int) -> decltype(std::declval<T>() + std::declval<U>() , std::true_type{});

template<typename T, typename U>
constexpr std::false_type check_addition(...);

template<typename T, typename U>
constexpr bool can_add = decltype(check_addition<T, U>(0))::value;

int main() {

    static_assert(can_add<int, float>);
    static_assert(!can_add<std::string, float>);

}

Идея состоит в том, чтобы использовать две перегрузки, одну с определенным аргументом типа (int в моем случае), а другую с многоточием,Когда мы вызываем эту перегруженную функцию с параметром int, компилятор сначала проверяет перегрузку int, где он должен выполнить требуемую проверку.

Оператор запятой используется для предоставления true_type кактип возвращаемого значения в приведенной проверке завершается успешно.

Если первая перегрузка SFINAE завершается неудачно, выбирается вторая перегрузка, которая всегда возвращает false_type.

...