Concepts/SFINAE ошибка с именем типа

Я пытаюсь сделать простой пример для себя, используя новый синтаксис концепции. Я решил проверить, определен ли для типа оператор operator(), и создал структуру для проверки этого с использованием парадигмы SFINAE, но я сталкиваюсь с проблемами типов. Вот мой код:

#include <utility>
#include <functional>

namespace Templates::Concepts {

    template<class type__>
    struct call_check {

        template<class type_ = type__>
        static auto check(std::nullptr_t) -> decltype(std::declval<type_>().operator()(), std::false_type(), std::true_type());

        template<class type_ = type__>
        static auto check(...) -> decltype(std::false_type());

        template<class type_ = type__>
        using type = decltype(check<type_>(nullptr));
    };

    template<typename type_>
    concept bool Callable = []() -> bool { typename call_check<type_>::type *t; return *t;};
}

Я начал без указателя 'typename' и просто

return call_check<type_>::type;,

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

concepts.h:20:78: error: ‘typename Templates::Concepts::call_check<yes>::type’ names ‘template<class type_> using type = decltype (check<type_>(nullptr))’, which is not a type,

и я застрял Честно говоря, я не совсем уверен, что самый правильный способ - реализовать эту проверку SFINAE, поэтому я не уверен, с чего начать. Любая помощь с парадигмой и / или с концепциями также будет оценена.

Я видел пример с чем-то вроде

std::declval<type_>()(std::declval<other>(), std::declval<op>()), ...

заменив первый элемент в вызове decltype первой проверки (для бинарных операторов), но мне было трудно понять, как это переводится в вызов функции. (Третий ответ сверху, для справки: Как проверить, существует ли оператор ==?).

2 ответа

Решение

С концепцией C++20 вы можете избежать "многословной и безобразной" парадигмы SFINAE.

Отказ от ответственности: следующий код совместим с Gnu Concepts (концепция C++20 еще не реализована).

Давайте определим следующую концепцию, которая проверяетoperator()существование по типуT:

template <typename T>
concept bool Callable() {
  return requires(T& t) {
    {t()}
  };
}

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

void bar(const Callable& t) {
  t();
}

GodBolt Полный пример.


Другое решение может быть получено просто сstd::is_invocable:

Например:

template <typename T>
struct Callable {
  static constexpr bool value = std::is_invocable_v<T>;
};

Это совместимо с C++17.

Пример тут.

Давайте рассмотрим ваш оригинальный код:

template<class type__>

Двойное подчеркивание в любом месте зарезервировано для реализации.

struct call_check {

    template<class type_ = type__>
    static auto check(std::nullptr_t) -> decltype(std::declval<type_>().operator()(), std::false_type(), std::true_type());

Вы обычно не хотите проверять, есть ли что-то operator(); Вы хотите проверить, может ли он вызываться без аргументов, и нет смысла в , std::false_type() часть, поэтому конечный тип возврата должен быть

-> decltype(std::declval<type_>()(), std::true_type())
   template<class type_ = type__>
   static auto check(...) -> decltype(std::false_type());

Это излишне многословно. decltype(std::false_type()) просто std::false_type, Также не требуется аргумент шаблона по умолчанию, так как вы его не используете, поэтому он становится

    template<class> 
    static std::false_type check(...);
   template<class type_ = type__>
   using type = decltype(check<type_>(nullptr));

И здесь нет причин делать этот шаблон псевдонимом. Это должен быть просто псевдоним:

using type = decltype(check<type__>(nullptr)); // modulo reserved identifier.

};

template<typename type_>
concept bool Callable = []() -> bool { typename call_check<type_>::type *t; return *t;};

Это неправильно во многих отношениях. Концепция переменных в TS должна быть инициализирована с помощью константного выражения, и лямбда-выражения нельзя использовать в константных выражениях до C++17. Более того, вы не вызывали лямбду, поэтому вы неявно преобразуете ее в указатель на функцию, а затем в bool, который всегда дает true, Наконец, на самом деле вызов лямбды будет неопределенным поведением, потому что он разыменовывает неинициализированный указатель.

Самый простой способ записать это

 template<typename type_>
 concept bool Callable = call_check<type_>::type::value;
Другие вопросы по тегам