【问题标题】:Concepts/SFINAE error with typename类型名的概念/SFINAE 错误
【发布时间】:2018-06-23 17:51:57
【问题描述】:

我正在尝试使用新概念语法为自己制作一个简单的示例。我决定测试一个类型是否定义了 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&lt;type_&gt;::type;,

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

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

我被困住了。坦率地说,我不完全确定实施此 SFINAE 检查的最正确方法是什么,所以我不确定从哪里开始。任何有关范例和/或概念的帮助也将不胜感激。

我确实看到了一个类似

的例子

std::declval&lt;type_&gt;()(std::declval&lt;other&gt;(), std::declval&lt;op&gt;()), ...

替换第一次检查的 decltype 调用中的第一项(对于二进制运算符),但我很难理解它是如何转换为函数调用的。 (楼上第三个答案,供参考:How to check whether operator== exists?)。

【问题讨论】:

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


    【解决方案1】:

    让我们处理您的原始代码:

    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之前的常量表达式中不能使用lambda。此外,您没有调用 lambda,因此您将其隐式转换为函数指针,然后转换为bool,这总是产生true。最后,实际调用 lambda 将是未定义的行为,因为它取消引用未初始化的指针。

    最简单的拼写方式是

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

    【讨论】:

      【解决方案2】:

      使用 C++20 概念,您可以避免“冗长而丑陋的”SFINAE 范式。

      免责声明:以下代码与 Gnu 概念兼容(C++20 概念尚未实现)。

      让我们定义以下 concept 来检查 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.

      【讨论】:

      • 占位符t的类型不用详细说明,可以是T
      • @MárioFeroldi 是的!谢谢
      • requires() { {T{}()} }; 要求 T 是默认可构造的,这可能不是有意的。我怀疑 requires(T&amp; t) { t(); } 更接近 OP 正在寻找的内容。
      • 谢谢!我想我把事情复杂化了。我以为代码必须编译,但似乎并非如此。
      猜你喜欢
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-14
      • 1970-01-01
      • 2017-03-12
      • 1970-01-01
      • 2021-11-28
      相关资源
      最近更新 更多