简体   繁体   English

使用犰狳库进行意外的(错误的)模板推导

[英]Unexpected (wrong) template deduction using armadillo library

I am writing a c++ template function, aimed at computing a function of a matrix, where the matrix type is a template parameter. 我正在编写一个c ++模板函数,旨在计算矩阵的函数,其中矩阵类型是模板参数。 When using it with the armadillo library, I get an unexpected failure in compilation. 与armadillo库一起使用时,编译时出现意外故障。 I am using armadillo 8.300 and gcc 7.2.0. 我正在使用犰狳8.300和gcc 7.2.0。 In the following a test program which illustrates the issue. 下面是说明问题的测试程序。

#include <armadillo>

arma::Mat<double> sq(const arma::Mat<double>& M)
{
  arma::Mat<double> res(M);
  res = res * M;
  return res;
}

template <class MatrixClass>
MatrixClass sqgen(const MatrixClass& M)
{
  MatrixClass res(M);
  res = res * M;
  return res;
}

int main()
{
  arma::Mat<double> id(3, 3, arma::fill::eye);
  arma::Mat<double> m(3, 3, arma::fill::ones);

  arma::Mat<double> m2(sq(m));
  arma::Mat<double> m_id2(sq(id - m));

  arma::Mat<double> m2gen(sqgen(m));
  arma::Mat<double> m_id2gen(sqgen(id - m)); // Error here
  return 0;
}

For illustration I defined two functions sq and sqgen which essentially do the same job. 为了说明起见,我定义了两个函数sqsqgen ,它们实际上完成了相同的工作。 sq is the explicit instantiation of sqgen when the template parameter MatrixClass is arma::Mat<double> . 当模板参数MatrixClassarma::Mat<double>时, sqsqgen的显式实例化。 The compilation with 编译用

g++ -std=c++14 -Wall -pedantic -O3 -o test test.cpp -lstdc++ -larmadillo

fails giving the error: 无法给出错误:

test.cpp: In instantiation of ‘MatrixClass sq(const MatrixClass&) [with MatrixClass = arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>]’:
test.cpp:24:36:   required from here
test.cpp:14:7: error: no match for ‘operator=’ (operand types are ‘arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>’ and ‘arma::enable_if2<true, const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times> >::result {aka const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times>}’)
   res = res * M;
   ~~~~^~~~~~~~~
In file included from /usr/include/armadillo:204:0,
                 from test.cpp:1:
/usr/include/armadillo_bits/eGlue_bones.hpp:22:7: note: candidate: arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>& arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>::operator=(const arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>&) <deleted>
 class eGlue : public Base<typename T1::elem_type, eGlue<T1, T2, eglue_type> >
       ^~~~~
/usr/include/armadillo_bits/eGlue_bones.hpp:22:7: note:   no known conversion for argument 1 from ‘arma::enable_if2<true, const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times> >::result {aka const arma::Glue<arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>, arma::glue_times>}’ to ‘const arma::eGlue<arma::Mat<double>, arma::Mat<double>, arma::eglue_minus>&’

The problem lies in the last call of sqgen . 问题出在sqgen的最后一次调用。 There is no problem in calling sqgen(m) , however the call sqgen(id - m) causes the error. 调用sqgen(m)没问题,但是调用sqgen(id - m)会导致错误。 Notice that, instead, using the call sq(id - m) is perfectly legal. 注意,相反,使用调用sq(id - m)是完全合法的。 Since the function sqgen should be, in the code above, exactly equal to sq , I presume that the compiler does not deduce correctly the template parameter MatrixClass in sqgen(id - m) . 由于在上面的代码中,函数sqgen应该完全等于sq ,因此我认为编译器没有正确推断sqgen(id - m)的模板参数MatrixClass In fact, upon substituting 实际上,当替代

arma::Mat<double> m_id2gen(sqgen(id - m));

with

arma::Mat<double> m_id2gen(sqgen(arma::Mat<double>(id - m)));

the code compiles correctly. 代码正确编译。

Template pattern matching matches exact types (and types of parent types if the exact type doesn't match). 模板模式匹配匹配精确类型(如果精确类型不匹配,则匹配父类型的类型)。

Overload resolution matches exact types, parent types, and types that can be converted to. 重载解析匹配确切的类型,父类型和可以转换为的类型。

Odds are that arma::Mat 's operations result in expression templates, which can be converted into matrices, but are not themselves matrices. 可能是arma::Mat的操作导致生成表达式模板,该模板可以转换为矩阵,但本身不是矩阵。 They exist so that you can take an entire line of matrix math, and efficiently not do it until you actually convert everything into a matrix. 它们的存在是为了让您可以学习整行矩阵数学,并且在您将所有内容都真正转换为矩阵之前才可以有效地做到。

Because sqgen takes an anything, in this case it tries to consume an expression template whose value is the difference between two matrices. 因为sqgen需要任何值,所以在这种情况下,它将尝试使用表达式模板,其值是两个矩阵之间的差。

You then create a temporary expression template instance with no arguments, multiply it with another expression template, assign to it, and return it. 然后,创建一个不带参数的临时表达式模板实例,将其与另一个表达式模板相乘,分配给它,然后返回它。 None of these make sense for an expression template. 这些对于表达式模板都没有意义。

This is a known problem with expression templates and generic code. 这是表达式模板和通用代码的已知问题。 Typically there are ways to force evaluation of expression templates. 通常,有几种方法可以强制评估表达模板。 Assinging them to a matrix does it (and is how sq works), casting them works, and in this case there is a .eval() member function which does it without having to name the type. 将它们关联到一个矩阵即可(以及sq工作方式),强制转换它们的工作方式,在这种情况下,有一个.eval()成员函数可以执行此操作而不必命名类型。

So, try 所以,尝试

arma::Mat<double> m_id2gen(sqgen((id - m).eval()));

The compiler is working fine. 编译器工作正常。 Armadillo does heavy optimization of expressions involving matrices, such as reordering multiplications, delaying evaluation, and more. Armadillo对涉及矩阵的表达式进行了大量优化,例如重新排序乘法,延迟求值等等。 This is all done via template metaprogramming. 这些都是通过模板元编程完成的。 The Armadillo matrix classes provide copy-assignment operators (eg, mat::operator=() ), which take other matrices and copy their data. Armadillo矩阵类提供了复制分配运算符(例如mat::operator=() ),该mat::operator=()采用其他矩阵并复制其数据。 There is, however, no overload of the operator=() which takes one of these expression templates. 但是,使用这些表达式模板之一的operator=()没有重载。 Hence the error about operand types are 'eGlue<...>...' and such. 因此,有关operand types are 'eGlue<...>...'的错误operand types are 'eGlue<...>...'等。

The quick fix to this is to add .eval() to the end of any expression, which forces evaluation of said expression. 对此的快速解决方案是将.eval()添加到任何表达式的末尾,这会强制评估该表达式。 So you'd do: 所以你会做:

res = (res * M).eval();
return res;

Or just: 要不就:

return (res * M).eval(); // I'm actually not sure if the eval() is necessary here.

Another option is to try doing in-place multiplication, which should also work fine. 另一个选择是尝试执行就地乘法,这也可以正常工作。 As in: 如:

res *= M;
return res;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM