编辑(3):将来可能会关注谁。我已经放弃了将朋友功能与CRTP结合使用的想法。虽然使用非朋友自由函数可能会产生额外的间接层次,但你不能(我已经学到了很多困难)实际上期望CRTP能够轻松替代传统的动态多态或重载机制。您将不了解如何定义和使用用户定义的type_traits,SFINAE,模板实例化订单等等 - 它是一种全有或全无的交易,您将被迫学习很多,乐观地说。
我已经更新了下面的代码示例。它显示了两个代数运算符operator+=()
和operator+()
的实现,以及一个简单的operator<<()
。为了使派生类的value_types在基类中已知,我另外定义了一个matrix_traits
类,它分别专门用于两个派生类。
编辑(2):事实证明,我实现friend operator<<()
的方式如下所述并不起作用,但会导致模糊的重载。有或没有type_traits。我的机智已经结束了。
编辑:经过一番调查,我更改了这个问题的标题,并会以不同的方式描述问题。
TL; DR :
最让我困惑的是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;
}