【问题标题】:Unexpected result of std::is_invocable over a template typestd::is_invocable 在模板类型上的意外结果
【发布时间】:2020-09-09 13:23:43
【问题描述】:

我有一个if constexpr 来检查一个类型是否与它自己相等。我用std::is_invocable_v<std::equal_to<>, T, T>

然而,当T 是一个不可比较结构的向量时,sn-p 会错误地返回 True。是有深层原因还是编译器错误?

下面是最小的例子。

#include <type_traits>
#include <iostream>
#include <vector>

class TNonComparable{};

int main()
{
    std::cout << std::is_invocable_v<std::equal_to<>, TNonComparable, TNonComparable> << "\n";
    // 0

    std::cout << std::is_invocable_v<
            std::equal_to<>,
            std::vector<TNonComparable>,
            std::vector<TNonComparable>
        > << "\n";
    // 1

    std::vector<TNonComparable> vec;
    // vec == vec;
    // (expected) compilation error
}

我检查了 Godbolt 的输出,所有最新版本的 g++ 和 clang 都是一样的。

【问题讨论】:

    标签: c++ templates typetraits


    【解决方案1】:

    我认为look at std::is_invocable 所做的事情非常重要:

    确定是否可以使用参数ArgTypes... 调用Fn。 形式上,确定INVOKE(declval&lt;Fn&gt;(), declval&lt;ArgTypes&gt;()...) 在被视为未评估时是否格式正确 操作数,其中INVOKECallable中定义的操作

    强调我的。

    这里要注意的重要部分是 std::equal_to&lt;&gt;std::is_invocable 中使用永远不会被计算,因为它是一个未计算的操作数。这意味着它只检查 operator== 是否存在,它对 std::vector&lt;&gt; 进行检查,而不是在评估的上下文中编译。

    【讨论】:

      【解决方案2】:

      我认为,这是正确的行为。

      在第一个std::is_invokable_v 中,检查TNonComparable 类型中是否存在operator==。它不存在 - 所以结果是 0。

      在第二种情况下,std::is_invokable_v 检查 std::vector 的相等运算符,它存在并且可以被调用。但是如果尝试调用它,它将无法编译,因为TNonComparable 类型没有operator==。但在你不尝试使用它之前,它不会产生错误。

      也许,在第二种情况下,您应该检查 std::vector 的 value_type:

      std::cout << std::is_invocable_v<
              std::equal_to<>,
              std::vector<TNonComparable>::value_type,
              std::vector<TNonComparable>::value_type
          > << "\n";
      // 0
      

      【讨论】:

      • 嗯,这听起来很合理,谢谢。您对如何自动执行深度检查有任何想法吗?我可能可以编写检测 ::value_type 和类似的递归检查器并递归检查它们的可比性,但它会在用户定义的复合对象上失败。
      • @IvanSmirnov:“你知道如何自动执行深度检查吗?”你不能制作使用 SFINAE 的东西不使用 SFINAE。如果vector 的相等运算符对SFINAE 不友好,那么您对vector 的相等运算符的测试也不会对SFINAE 友好。最好只接受您提供的代码的限制并将这些限制转发给用户。
      【解决方案3】:

      有一些时间,我已经编写了解决方法

      namespace detail
      {
          template<typename T, typename=void>
          struct has_value_type : std::false_type {};
      
          template<typename T>
          struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
      
          template<typename T, typename=void>
          struct is_pair : std::false_type {};
      
          template<typename T, typename U>
          struct is_pair<std::pair<T, U>, void> : std::true_type {};
      
          template<typename T, typename=void>
          struct is_tuple : std::false_type {};
      
          template<typename ...Args>
          struct is_tuple<std::tuple<Args...>, void> : std::true_type {};
      }
      
      template<typename T, typename=void>
      struct has_euqual_operator : std::false_type {};
      
      template<typename T>
      struct has_euqual_operator<
              T, 
              std::void_t<
                  std::enable_if_t<
                      !detail::is_pair<T>::value 
                      && !detail::has_value_type<T>::value 
                      && !detail::is_tuple<T>::value>,
                  decltype(std::declval<T>() == std::declval<T>())
              >
          > : std::true_type 
      {};
      
      template<typename T>
      struct has_euqual_operator<
              T, 
              std::enable_if_t<has_euqual_operator<typename T::value_type>::value>
          > : std::true_type 
      {};
      
      template<typename T>
      struct has_euqual_operator<
              T, 
              std::enable_if_t<has_euqual_operator<typename T::first_type>::value
                  && has_euqual_operator<typename T::second_type>::value
              >
          > : std::true_type 
      {};
      
      template<typename ...Args>
      struct has_euqual_operator<
              std::tuple<Args...>, 
              std::enable_if_t<(... && has_euqual_operator<Args>::value)>
          > : std::true_type 
      {};
      

      https://godbolt.org/z/rcejhx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-10
        • 1970-01-01
        • 1970-01-01
        • 2017-05-15
        • 1970-01-01
        • 2012-09-10
        • 2015-09-21
        • 1970-01-01
        相关资源
        最近更新 更多