类型名称的Concepts / SFINAE错误

时间:2018-06-23 17:51:57

标签: c++ templates sfinae c++-concepts declval

我正在尝试使用新概念语法为自己做一个简单的例子。我决定测试一种类型是否定义了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;

但是我收到了名称相关的类型错误。添加类型名称后,我现在收到

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调用中的第一项(对于二进制运算符),但是我很难理解如何将其转换为函数调用。 (自上而下的第三个答案,供参考:How to check whether operator== exists?)。

2 个答案:

答案 0 :(得分:3)

让我们解决您的原始代码:

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中的变量概念必须使用常量表达式初始化,并且lambda不能在C ++ 17之前的常量表达式中使用。而且,您没有调用 lambda,因此您将其隐式转换为函数指针,然后转换为bool,该指针始终产生true。最后,实际上调用lambda是未定义的行为,因为它取消引用了未初始化的指针。

最简单的拼写方法是

 template<typename type_>
 concept bool Callable = call_check<type_>::type::value;

答案 1 :(得分:1)

使用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 Complete Example


只需使用std::is_invocable即可获得另一种解决方案:

例如:

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

这与 C ++ 17 兼容。

Example here