C++隐式模板实例化

Vic*_*Liu 7 c++ templates metaprogramming

我目前有类层次结构

MatrixBase -> DenseMatrix
           -> (other types of matrices)
           -> MatrixView -> TransposeView
                         -> DiagonalView
                         -> (other specialized views of matrices)
Run Code Online (Sandbox Code Playgroud)

MatrixBase是一个抽象类,它强制实现者定义operator()(int,int)等等; 它代表二维数字数组.MatrixView表示一种(可能是可变的)查看矩阵的方式,例如转置它或采用子矩阵.关键MatrixView是能够说出类似的话

Scale(Diagonal(A), 2.0)
Run Code Online (Sandbox Code Playgroud)

其中Diagonal返回DiagonalView对象,它是一种轻量级的适配器.

现在这是问题所在.我将使用一个非常简单的矩阵运算作为例子.我想定义一个像这样的函数

template <class T>
void Scale(MatrixBase<T> &A, const T &scale_factor);
Run Code Online (Sandbox Code Playgroud)

这就是名字暗示的显而易见的事情.我希望能够传递一个诚实到善的非视图矩阵,或者一个子类的实例MatrixView.上面写的原型不适用于诸如此类的语句

Scale(Diagonal(A), 2.0);
Run Code Online (Sandbox Code Playgroud)

因为DiagonalView返回的对象Diagonal是临时的,并且Scale采用非const引用,这不能接受临时的.有没有办法让这项工作?我试图使用SFINAE,但我不太了解它,我不确定这是否能解决问题.对我来说很重要的是,可以在不提供显式模板参数列表的情况下调用这些模板化函数(我想要隐式实例化).理想情况下,上述陈述可以按照书面形式运作


编辑:(后续问题)

由于sbi在下面回答了关于右值引用和临时值的问题,有没有办法定义两个版本的Scale,一个为非视图采用非const rvalue引用,另一个采用按值传递视图?问题是在编译时区分这两者,使隐式实例化起作用.


更新

我已经将类层次结构更改为

ReadableMatrix
WritableMatrix : public ReadableMatrix
WritableMatrixView
DenseMatrix : public WritableMatrix
DiagonalView : public WritableMatrixView
Run Code Online (Sandbox Code Playgroud)

原因WritableMatrixView不同于WritableMatrix视图必须通过const引用传递,而矩阵本身必须由非const引用传递,因此访问器成员函数具有不同的常量.现在像Scale这样的函数可以定义为

template <class T>
void Scale(const WritableMatrixView<T> &A, const T &scale_factor);
template <class T>
void Scale(WritableMatrix<T> &A, const T &scale_factor){
    Scale(WritableMatrixViewAdapter<T>(A), scale_factor);
}
Run Code Online (Sandbox Code Playgroud)

请注意,有两个版本,一个用于const视图,另一个用于实际矩阵.这意味着对于类似的功能Mult(A, B, C),我将需要8次重载,但至少它可以工作.然而,什么不起作用是在其他功能中使用这些功能.你看,每个View类都包含了View它所看到的成员; 例如,在表达式中Diagonal(SubMatrix(A)),Diagonal函数返回一个类型的对象 DiagonalView<SubMatrixView<T> >,它需要知道完全派生的类型A.现在,假设在Scale我调用其他类似的函数,它采用基本视图或矩阵引用.那会失败,因为所需的构造需要ViewScale的参数的派生类型; 它没有的信息.仍在努力寻找解决方案.


更新

我已经使用了Boost的enable_if的本土版本,在两个不同版本的函数之间进行选择Scale.它归结为标记我的所有矩阵和视图类,其中包含额外的typedef标记,指示它们是可读写的还是视图或非视图.最后,我仍然需要2 ^ N个重载,但现在N只是非const参数的数量.对于最终结果,请参见此处(不太可能再次进行认真修改).

sbi*_*sbi 7

这与模板无关.你的榜样

Scale(Diagonal(A), 2.0);
Run Code Online (Sandbox Code Playgroud)

可以推广到

f(g(v),c);
Run Code Online (Sandbox Code Playgroud)

在C++ 03中,这需要为f()每个副本或每个const引用传递第一个参数.原因是g()返回一个临时值,一个右值.但是,rvalues仅绑定到const引用,但不绑定到非const引用.这与模板,SFINAE,TMP或其他内容无关.这就是语言(目前)的方式.

这背后还有一个理由:如果g()返回一个临时的,并f()修改那个临时的,那么没有人有机会"看到"修改后的临时.因此修改是徒劳的,整个事情很可能是一个错误.

据我所知,在你的情况下,结果g()是一个临时的,它是一个其他对象(v)的视图,所以修改它会修改v.但如果是这种情况,在当前的C++中,结果g()必须是const(这样它可以被绑定到const引用或者它必须被复制.因为const"闻起来"对我来说是错误的,使得该视图便宜复制可能是最好的事.

但是,还有更多.C++ 1x将引入所谓的右值引用.我们所知的"引用"将被分为左值引用或右值引用.您将能够使用rvalue引用函数,甚至基于"l/rvalue-ness"进行重载.这被认为是允许类设计者重载rvalue右侧的复制ctor和赋值并让它们"窃取"右侧的值,这样复制rvalues会更便宜.但你可能会用它来Scale取一个右值并修改它.

不幸的是,你的编译器很可能还不支持rvalue引用.


编辑(后续问题):

你不能重载f(T)f(T&)实现你想要什么.虽然只有前者将用于rvalues,f但是左值可以同样很好地绑定到任一个参数,因此使用左值调用它是不明确的,并导致编译时错误.

但是,有一个重载的错误DiagonalView:

template <class T>
void Scale(MatrixBase<T> &matrix, const T &scale_factor);

template <class T>
void Scale(DiagonalView<T> view, const T &scale_factor);
Run Code Online (Sandbox Code Playgroud)

有什么我想念的吗?


另一个编辑:

我需要一个可笑的大量重载,因为目前有超过5个视图,并且有几十个函数,如Scale.

然后,您需要将可以以相同方式处理的那些类型组合在一起.您可以使用一些简单的模板元素来进行分组.脱离我的头顶:

template<bool B>
struct boolean { enum { result = B }; };

template< typename T >
class some_matrix {
  public:
    typedef boolean<false> is_view;
  // ...
};

template< typename T >
class some_view {
  public:
    typedef boolean<true> is_view;
  // ...
};

namespace detail {
  template< template<typename> class Matrix, typename T >
  void Scale(Matrix<T>& matrix, const T& scale_factor, boolean<true>)
  {
    /* scaling a matrix*/
  }
  template< template<typename> class Matrix, typename T >
  void Scale(View<T>& matrix, const T& scale_factor, boolean<true>)
  {
    /* scaling a view */
  }
}

template< template<typename> class Matrix, typename T >
inline void Scale(Matrix<T>& matrix, const T& scale_factor)
{
  detail::Scale( matrix, scale_factor, typename Matrix<T>::is_view() );
}
Run Code Online (Sandbox Code Playgroud)

这种特殊的设置/分组可能不完全符合您的需求,但您可以以适合自己的方式设置这样的设置.


Bjö*_*lex 1

解决这个问题的一个简单方法是使用boost::shared_ptr< MatrixBase<T> >而不是引用。