【问题标题】:C++ template metafunction on instantiated object of a template class模板类的实例化对象上的 C++ 模板元函数
【发布时间】:2013-10-13 15:06:53
【问题描述】:

我希望这个问题不会过于复杂。我意识到元编程作用于类型而不是那些类型的对象。但是,我仍然试图通过 1) 从类中检索类型信息,然后 2) 对该类型信息的元函数来实现相同的结果。

我的情况的解释如下,并带有简化的代码摘录:

我有一个矩阵模板类,我称之为 Matrix_Base。有点类似于 Eigen 采用的方法,我允许矩阵大小有两种可能性——要么在编译时固定,要么在运行时固定。 Matrix_Base 的简化声明为:

template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1,>
class Matrix_Base{
/* 
...
*/
};

运行时大小的矩阵由参数 0 表示。

检查矩阵的运行时与编译时大小相当简单(使用 boost::mpl):

            typedef typename mpl::if_<
                typename mpl::or_<
                typename mpl::equal_to<
                typename mpl::int_<rows_>::type,
                mpl::int_<0>
                >::type,
                typename mpl::equal_to <
                typename mpl::int_<cols_>::type,
                mpl::int_<0>
                >
                >::type,
                mpl::true_,
                mpl::false_
                >::type runtime_size_type;

这已经过测试并且工作正常。我的麻烦从这里开始......

目前,我正在以下列方式使用上述 boost::mpl 代码:

namespace internal {
template <uint32_t rows = 1, uint32_t cols_ = 1>
struct Runtime_Size_Helper {
typedef typename mpl::if_< 
// REST OF THE BOOST::MPL code here //
>::type runtime_size_t
bool value() { return runtime_size_t::value;}
};
} // namespace
template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1>
class Matrix_Base{
// ...
static constexpr uint32_t rows = rows_;
static constexpr uint32_t cols = cols_;
bool is_runtime_sized;
// ...
};

template <typename T, uint32_t R, uint32_t C>
bool Matrix_Base<T,R,C>::is_runtime_sized = internal::Runtime_Size_Helper<R,C>::value();

这使得 mpl 函数的结果成为 Matrix_Base 类的成员。到目前为止一切顺利。

我想使用某种形式的间接来确定 runtime_size_type 的值,方法是将其传递给实例化对象。根据示例代码,确定这一点所需的唯一信息是 col 和 row 的 uint32_t 参数。

对于实例化的 Matrix_Base 对象,相关信息永远不会改变其编译类型值。矩阵的大小将是不可变的;大小将通过模板参数设置,或者——对于运行时大小的矩阵——通过构造函数设置。在这两种情况下,模板参数都是固定的,并且是类型信息的一部分。我将此信息保存为类中的静态变量,我什至尝试添加一个带有所有模板参数的 typedef 作为 my_type typename typename Matrix_Base&lt;T,rows_, cols_, ...&gt; my_type 但我似乎无法弄清楚如何编写一个可以传递的元函数Matrix_Base 对象(显然作为引用或指针)并重新提取相关信息。

如果它们能提供必要的功能,我完全愿意合并(其他)boost 库。

希望这很清楚。请让我知道这里是否有什么不清楚或只是愚蠢的地方。

最好的问候, 什穆尔

编辑了文本以更清楚地说明问题

【问题讨论】:

    标签: c++ templates boost metaprogramming boost-mpl


    【解决方案1】:

    做你想做的事情的最快方法(你没有真正详细指定它)是让你的矩阵类模板继承自一个存储类模板,并根据你的 MPL 谓词是否专门化存储类返回真或假

    #include <array>
    #include <iostream>
    #include <vector>
    #include <boost/mpl/bool.hpp>
    #include <boost/mpl/comparison.hpp>
    #include <boost/mpl/if.hpp>
    #include <boost/mpl/int.hpp>
    #include <boost/mpl/logical.hpp>
    
    using namespace boost;
    
    // in C++98, use 
    // template<int R, int C>
    // struct is_runtime_sized: mpl::if<
    // ...
    // >::type {};
    template<int R, int C>
    using is_runtime_sized = typename mpl::if_<
        mpl::or_<
            mpl::equal_to<mpl::int_<R>, mpl::int_<0>>,
            mpl::equal_to<mpl::int_<C>, mpl::int_<0>>
        >,
        mpl::true_, mpl::false_
    >::type;
    

    请注意,我已经消除了一些不必要的 typename 出现,以使 MPL 谓词更具可读性。

    template<class T, int R, int C, bool = is_runtime_sized<R, C>::value>
    struct MatrixStorage
    {
        MatrixStorage() = default;
        MatrixStorage(int r, int c): data_(r * c) {} // zero-initializes
    protected:    
        std::vector<T> data_;
    };
    
    template<class T, int R, int C>
    struct MatrixStorage<T, R, C, false>
    {
        MatrixStorage() = default;
        MatrixStorage(int, int): data_{} {} // zero-initializes
    protected:    
        std::array<T, R * C> data_;    
    };
    

    在这里,我已经拆分了动态和静态存储矩阵的实现。前者使用std::vector,后者使用std:array。与 Eigen 类似,两者都有一个默认构造函数,并且都有一个构造函数,该构造函数采用零初始化的矩阵维度。

    template<class T, int R = 0, int C = 0>
    struct Matrix: public MatrixStorage<T, R, C>
    {
        Matrix() = default;
        // In C++98, write:
        // Matrix(int r, int c): MatrixStorage<T, R, C>(r, c) {}
        using MatrixStorage<T, R, C>::MatrixStorage;
        int size() const { return this->data_.size(); }
    };
    

    实际的Matrix 类继承自MatrixStorage,并根据当前存储的数据返回size()

    int main()
    {
        Matrix<int> m_dyn(3, 3);
        std::cout << m_dyn.size() << "\n"; // 9
    
        Matrix<int, 2, 2> m_stat;
        std::cout << m_stat.size() << "\n"; // 4
    }
    

    Live Example。如您所见,动态分配的矩阵大小为 9,静态大小的矩阵大小为 4。请注意,上述代码中有两个小的 C++11 特性,如果需要,您可以轻松解决使用 C++11。

    【讨论】:

    • 再次感谢您的回答。正如您所说,我想我的问题背后的为什么不清楚。它与存储无关(尽管这是一个明智且合理的假设)。事实上,在阅读了您的回复后,我意识到我遇到了一些类似的问题,这些问题都会从这种方法中受益。
    • @ShmuelLevine 重要的一点是运行时对象不能在编译时处理。处理运行时/编译时代码的另一种方法是使用constexpr,但这种方法仅适用于 C++14(目前仅由 Clang SVN 主干支持)。
    • 这是有道理的。就我而言,我使用的是constexpr,并且所有必要的信息 is 在编译时都可用,但我使用的是 C++11,而不是 C++14。无论如何,您的上述建议将解决我一直试图解决的一些问题。
    • @ShmuelLevine 你可以在 C++11 中使用一些简单的constexpr 东西,比如存储动态大小的构造函数。我想更多的是constexpr矩阵功能,C++11风格constexpr的递归结构太麻烦了。
    猜你喜欢
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    相关资源
    最近更新 更多