【问题标题】:Checking type with template C++使用模板 C++ 检查类型
【发布时间】:2019-07-12 19:48:42
【问题描述】:

我正在研究 Matrix 类的实现(在 Stroustrup 的书 TC++PL 4th ed. 中有解释),但我无法真正理解某些段落。

我找到了这段代码:

文件特征.h -> https://github.com/statslabs/matrix/blob/master/include/slab/matrix/traits.h

文件 matrix.h -> https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix.h

在 matrix.h 中有一个带有Enable_if 的函数(和其他许多函数一样):

template<typename T, std::size_t N>
template<typename M, typename F>
Enable_if<Matrix_type<M>(), Matrix<T, N> &> Matrix<T, N>::apply(const M &m, F f) {
    /// Some code...
}

我认为Enable_if 说:如果(MMatrix),则将应用程序的返回类型声明为Matrix&lt;T, N&gt;&amp;

然后,我想知道Matrix_type&lt;M&gt;() 是如何工作的,所以我转到traits.h,然后阅读:

struct substitution_failure {};

template <typename T>
struct substitution_succeeded : std::true_type {};

template <>
struct substitution_succeeded<substitution_failure> : std::false_type {};

template <typename M>
struct get_matrix_type_result {
  template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
  static bool check(const Matrix<T, N> &m);

  template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
  static bool check(const MatrixRef<T, N> &m);

  static substitution_failure check(...);

  using type = decltype(check(std::declval<M>()));
};

template <typename T>
struct has_matrix_type
    : substitution_succeeded<typename get_matrix_type_result<T>::type> {};

template <typename M>
constexpr bool Has_matrix_type() {
  return has_matrix_type<M>::value;
}

template <typename M>
using Matrix_type_result = typename get_matrix_type_result<M>::type;

template <typename M>
constexpr bool Matrix_type() {
  return Has_matrix_type<M>();
}

前 3 个结构描述成功和失败的情况,template&lt;&gt;substitution_succeeded 的特化,它表示:如果 substitution_succeeded 的类型是 substitution_failure,则 "return" false 否则 "return" true。 我希望我说的是正确的。

现在,get_matrix_type_result 完全不为人所知。我不明白为什么它使用可变参数函数 (check(...)),declvaldecltype 在这段代码中做了什么,以及 check 如何返回 bool 或 substitution_failure。为什么不只是bool

谢谢。

【问题讨论】:

    标签: c++ c++11 matrix sfinae decltype


    【解决方案1】:

    现在,get_matrix_type_result 是完全模糊的。我不明白为什么它使用可变参数函数(check(...)),在这段代码中 declval 和 decltype 做了什么,以及 check 怎么可能返回 bool 或“substitution_failure”。为什么不只是 bool?

    重要的一点是Enable_ifstd::enable_if 从 C++11 开始)旨在启用或不启用某些东西(模板函数、模板类特化、模板方法、模板变量特化)。

    实际上是一个名为 SFINAE(替换失败不是错误)的 C++ 原则,它说,在某些地方,如果没有发生替换,它只是一个软错误,而不是硬错误,并且编译可以继续。

    在您的情况下,type 定义如下

    using type = decltype(check(std::declval<M>()));
    

    其中decltype()“返回”参数的类型;在这种情况下,调用 check() 返回的类型为 M 类型的假设对象(类的模板参数)。

    你可以写

    using type = decltype(check(M{}));
    

    传递M 类型的对象。但这仅适用于默认可构造的类型。问题是:如果我们不知道如何构造该类型的对象,如何在decltype() 参数(具有推断类型的专有功能,而不是执行指令)中使用泛型类型的对象?

    解决方案是一个函数只声明(未定义)如下

    template<class T>
    typename std::add_rvalue_reference<T>::type declval() noexcept;
    

    这是一个拥有T(或更好:T &amp;)类型的对象的技巧,即使你不知道如何构造它。

    回到check,你有它的三个版本(只声明:在decltype()内部使用;我们只对返回的类型感兴趣,所以不需要执行它们,所以不需要定义它们):

    1) 第一个接受Matrix&lt;T, N&gt; (Enable_if) 如果N &gt;= 1

    template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
    static bool check(const Matrix<T, N> &m);
    

    如果您使用Matrix&lt;T, 0&gt; 调用check(),则Enable_if 不会返回任何内容,因此您会遇到替换失败(定义模板参数的默认值),因此此版本的check() 未启用

    2) 第二个接受MatrixRef&lt;T, N&gt; (Enable_if) 如果N &gt;= 1

    template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
    static bool check(const MatrixRef<T, N> &m);
    

    再一次:如果你用MatrixRef&lt;T, 0&gt; 调用check()Enable_if 什么也不返回,所以你有一个替换失败(定义模板参数的默认值)所以这个版本的check() 没有启用

    3) 第三个接受所有内容并曾经 启用

    static substitution_failure check(...);
    

    结论:

    1) 如果MMatrix&lt;T, N&gt;(或可转换为Matrix&lt;T, N&gt; 的对象),对于一些T 和一些NN &gt;= 1,编译器可以在版本(1) 之间进行选择和check() 的版本(3)并选择版本(1),因为更专业,返回bool,所以type 变成bool

    2) 如果MMatrixRef&lt;T, N&gt;(或可转换为MatrixRef&lt;T, N&gt; 的对象),对于一些T 和一些NN &gt;= 1,编译器可以在版本(2) 之间进行选择和check() 的版本(3)并选择版本(2),因为更专业,返回bool,所以type 变成bool

    3) 如果M 不能转换为Matrix&lt;T, N&gt;MatrixRef&lt;T, N&gt;,使用N &gt;= 1,编译器只能选择返回substitution_failure 的版本(3),所以type变成substitution_failure

    题外话:您向我们展示的代码在我看来有点过于复杂。

    例如,如果你重写get_matrix_type_result如下

    template <typename M>
    struct get_matrix_type_result {
      template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
      static std::true_type check(const Matrix<T, N> &m);
    
      template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
      static std::true_type check(const MatrixRef<T, N> &m);
    
      static std::false_type check(...);
    
      using type = decltype(check(std::declval<M>()));
    };
    

    你知道type是你在has_matrix_type中需要的类型,可以定义如下

    template <typename T>
    struct has_matrix_type
        : public get_matrix_type_result<T>::type
     { };
    

    完全避免substitution_failuresubstitution_succeded

    但是,也许,以这种方式编写代码是为了满足其他需求。

    【讨论】:

    • 我全都搞定了!非常非常非常感谢。关于代码,我不知道为什么写得这么复杂,我只是一个学生,我没有C++的专业背景(我正在努力学习它)。这本书说,“屏蔽”代码是一种很好的做法(没有过多的抽象层次),这样用户就看不到实现的细节,而且更具可扩展性。我不知道,也许是因为如果你想实现一个异常或一些代码,你可以在substitute_failure 或substitution_succeded 中而不是在std::true_type 或std::false_type 中。
    猜你喜欢
    • 2017-05-08
    • 1970-01-01
    • 2014-03-31
    • 2015-07-20
    • 2017-10-18
    • 2016-03-16
    • 2013-10-13
    • 1970-01-01
    • 2023-03-25
    相关资源
    最近更新 更多