CRTP:朋友功能和SFINAE的习惯使用?

时间:2016-02-19 02:39:54

标签: c++ templates metaprogramming typetraits

编辑(3):将来可能会关注谁。我已经放弃了将朋友功能与CRTP结合使用的想法。虽然使用非朋友自由函数可能会产生额外的间接层次,但你不能(我已经学到了很多困难)实际上期望CRTP能够轻松替代传统的动态多态或重载机制。您将了解如何定义和使用用户定义的type_traits,SFINAE,模板实例化订单等等 - 它是一种全有或全无的交易,您将被迫学习很多,乐观地说。

我已经更新了下面的代码示例。它显示了两个代数运算符operator+=()operator+()的实现,以及一个简单的operator<<()。为了使派生类的value_types在基类中已知,我另外定义了一个matrix_traits类,它分别专门用于两个派生类。

编辑(2):事实证明,我实现friend operator<<()的方式如下所述并不起作用,但会导致模糊的重载。有或没有type_traits。我的机智已经结束了。

编辑:经过一番调查,我更改了这个问题的标题,并会以不同的方式描述问题。

TL; DR

  1. 如何在CRTP上下文中习惯使用友元函数?
  2. SFINAE何时与模板功能/类别结合使用?
  3. 最让我困惑的是different answer关注operator +()的实现。

    由于朋友功能与CRTP和SFINAE的使用都很棘手,我花了很长时间才弄清楚问题是什么以及如何描述它们。

    我想通过导出&#34; Dynamic_Matrix&#34;来使用奇怪的重复模板模式(CRTP)来实现Matrix类。和一个&#34; Static_Matrix&#34;来自基类&#34; Matrix_base&#34;。 (我原本打算打电话给他们&#34; Matrix&#34;,但这并没有太好。)

    另外,我尝试实施operator+=()operator+()(就operator+=()而言)和operator<<()

    通过大修和简化模板签名解决了我未解决的模板参数的原始问题。他们现在是:

    template <typename MatrixType>
        class Matrix_base;
    template <typename T, template <typename, typename...> class Container = std::vector>
        class Dynamic_matrix;
    template <typename T, std::size_t Rows, std::size_t Columns>
        class Static_matrix;
    

    问题1:朋友功能。 基类的友元函数不是派生类的朋友。 事实上,基类本身在派生类中被声明为朋友,这在这里没有用。相反,clang ++ 3.8.0会在基类中编译{strong>内联定义的friend std::ostream& operator<<(),而由于成员,g ++ 4.9.2(及更高版本)不会派生类的变量是私有的。实际上我需要一段时间才能注意到这一点。在这种情况下,如果引用对基类或派生类(即模板参数)的引用并不重要。两者都适用于clang ++,但不适用于g ++。

    的工作原理是前向声明运算符&lt;&lt;,分别在两个派生类中与模板特化friend std::ostream& operator<<(std::ostream&, Derived const&);建立联系,并在其函数体下定义它。这并不是什么不寻常的事情,但是你经常会将这种情况与模板类结合起来,这让我想到了第二个问题&#34;。 编辑:没有。如果我从类成员函数中尝试std::cout << "text\n";,这将导致对运算符&lt;&lt;&lt;。

    的模糊调用。

    问题2:SFINAE与免费功能相结合。 或者更确切地说,何时使用它?

    如果Derived& operator+=(Derived const&)是基类的公共成员函数,我们可以将operator +()实现为签名的免费(非朋友)函数

    template <typename Derived>
        Derived operator+(Derived lhs, Derived const &rhs);
    

    template <typename Derived>
        typename std::enable_if< std::is_base_of <Base <Derived>, Derived>::value, Derived>::type
        operator+(Derived lhs, Derived const &rhs);
    

    虽然后者可能更多&#34;正确&#34;但我无法将此SFINAE方法与朋友功能结合使用,因为我无法转发声明此类功能签名(在不完整类型上输入特征表达式)。 所以一般来说,如果我不能使用SFINAE进行免费的模板功能,我可以真实地遇到什么麻烦?我可以通过重载解决方案遇到麻烦吗?或者......?

    感谢任何建议!

    以下是一些功能代码,其中包含一些注释:

    Matrix.hpp:

    #ifndef MATRIX_HPP
    #define MATRIX_HPP
    
    #include <array>
    #include <string>
    #include <cassert>
    #include <vector>
    #include <initializer_list>
    #include <iterator>
    #include <iostream>
    #include <algorithm>
    #include <utility>
    #include <type_traits>
    
    namespace mbw
    {
        // Forward declarations of Base and Derived types:
        template <typename MatrixType>
            class Matrix_base;
        template <typename T, template <typename, typename...> class Container = std::vector>
            class Dynamic_matrix;
        template <typename T, std::size_t Rows, std::size_t Columns>
            class Static_matrix;
    
        // Forward declarations of Type traits:
        template <typename MatrixType>
            struct matrix_traits;
    
        template <typename MatrixType>
            class Matrix_base
            {
                public:
    
                    // make "value_type" known via an instance of template struct
                    using value_type = typename matrix_traits<MatrixType>::value_type;
    
                    std::size_t rows() const noexcept { return this->self()->rows_; }
                    std::size_t columns() const noexcept { return this->self()->columns_; }
    
                    value_type const* begin() const noexcept { return std::begin(this->self()->values_); }
                    value_type* begin() noexcept { return std::begin(this->self()->values_); }
                    value_type const* end() const noexcept { return std::end(this->self()->values_); }
                    value_type* end() noexcept { return std::end(this->self()->values_); }
    
                    bool operator==(Matrix_base const& rhs) { return this->self()->values_ == rhs.self()->values_; }
                    bool operator!=(Matrix_base const& rhs) { return this->self()->values_ != rhs.self()->values_; }
    
                    MatrixType& operator+=(MatrixType const&);
                private:
                    MatrixType* self() noexcept { return static_cast<MatrixType*>(this); }
                    MatrixType const* self() const noexcept { return static_cast<MatrixType const*>(this); }
            };
    
        template <typename T, template <typename, typename...> class Container>
            class Dynamic_matrix : public Matrix_base <Dynamic_matrix <T, Container>>
        {
            public:
                using value_type = typename matrix_traits<Dynamic_matrix>::value_type;
    
                Dynamic_matrix() = default;
                Dynamic_matrix(std::size_t r, std::size_t c, std::initializer_list<T> const &list)
                    : rows_{r}, columns_{c}, values_(std::begin(list), std::end(list)) {}
                template <typename InputIt>
                    Dynamic_matrix(InputIt first, InputIt last) : values_(first, last) {}
    
                ~Dynamic_matrix() = default;
    
                Dynamic_matrix(Dynamic_matrix const&) = default;
                Dynamic_matrix& operator=(Dynamic_matrix const&) = default;
    
                Dynamic_matrix(Dynamic_matrix&&) = default;
                Dynamic_matrix& operator=(Dynamic_matrix&&) = default;
    
            private:
                friend class Matrix_base <Dynamic_matrix <T, Container>>;
    
                std::size_t rows_;
                std::size_t columns_;
                Container<T> values_;
        };
    
        template <typename T, std::size_t Rows, std::size_t Columns>
            class Static_matrix : public Matrix_base <Static_matrix <T, Rows, Columns>>
        {
            public:
                using value_type = typename matrix_traits<Static_matrix>::value_type;
    
                Static_matrix() = default;
                // Without this type trait expression, this constructor will always be preferred
                // to the copy constructor:
                template <typename... U, typename = 
                    typename std::enable_if <!std::is_same <typename std::remove_reference <typename std::common_type
                                    <U...>::type >::type, Static_matrix >::value >::type >
                    explicit Static_matrix(U&&... values) : values_{{std::forward<U>(values)...}} {}
    
                template <typename InputIt>
                    Static_matrix(InputIt first, InputIt last)
                    {
    #ifndef NDEBUG
                        if (static_cast<decltype(values_.size())>(std::distance(first, last)) > values_.size())
                        {
                            std::string msg{std::to_string(std::distance(first, last)) + " elements exceed maximum of "
                                + std::to_string(values_.size())};
                            throw std::length_error{msg};
                        }
    #endif
                        std::copy(first, last, begin(values_));
                    }
                ~Static_matrix() = default;
    
                Static_matrix(Static_matrix const&) = default;
                Static_matrix& operator=(Static_matrix const&) = default;
    
                Static_matrix(Static_matrix&&) = default;
                Static_matrix& operator=(Static_matrix&&) = default;
    
            private:
                friend class Matrix_base <Static_matrix <T, Rows, Columns>>;
    
                static constexpr std::size_t rows_ = Rows;
                static constexpr std::size_t columns_ = Columns;
                std::array<T, Rows*Columns> values_;
        };
    
    
        // Specializations of type traits:
        template <typename T, template <typename, typename...> class Container>
            struct matrix_traits<Dynamic_matrix<T, Container>>
            {
                using value_type = T;
            };
        template <typename T, std::size_t Rows, std::size_t Columns>
            struct matrix_traits<Static_matrix<T, Rows, Columns>>
            {
                using value_type = T;
            };
    
    
        // Implementations of member functions of Matrix_base:
        template <typename MatrixType>
            MatrixType& Matrix_base<MatrixType>::operator+=(MatrixType const &rhs)
            {
                auto it = std::begin(rhs.self()->values_);
                std::for_each(begin(this->self()->values_), end(this->self()->values_), [&it](auto& el)
                        { 
                            el += *it;
                            std::advance(it, 1);
                        });
                return *(this->self());
            }
    
    
        // Implementations of free functions:
        template <typename MatrixType>
            typename std::enable_if< std::is_base_of <Matrix_base <MatrixType>, MatrixType>::value, MatrixType>::type
            operator+(MatrixType lhs, MatrixType const &rhs)
            {
                return lhs += rhs;
            }
    
        template <typename MatrixType>
            typename std::enable_if< std::is_base_of< Matrix_base< MatrixType >, MatrixType >::value, std::ostream>::type&
            operator<<(std::ostream &os, MatrixType const& m)
            {
                std::copy(m.begin(), m.end(),       // no ADL :(
                        std::ostream_iterator<typename MatrixType::value_type>{os, " "});
                return os;
            }
    }
    #endif
    

    main.cpp中:

    #include <iostream>
    #include "Matrix.hpp"
    
        int
    main ()
    {
        mbw::Dynamic_matrix<int> m{ 2, 2, { 1,1,1,1 } };
        mbw::Dynamic_matrix<int> n{ 2, 2, { 2,2,2,2 } };
        std::cout << m << '\n';
        std::cout << n << '\n';
        m += n;
        std::cout << m << '\n';
        std::cout << (m+n) << "\n\n";
    
        mbw::Static_matrix<int, 2, 2> o { 3,3,3,3 };
        mbw::Static_matrix<int, 2, 2> p {o};
        std::cout << o << '\n';
        std::cout << p << '\n';
        o += p;
        std::cout << o << '\n';
        std::cout << (o+p) << '\n';
        return 0;
    }
    

0 个答案:

没有答案