我了解执行以下操作:
auto&& x = Matrix1() + Matrix2() + Matrix3();
std::cout << x(2,3) << std::endl;
如果矩阵运算使用表达式模板(例如boost::ublas
),将导致静默运行时错误。
有没有什么方法可以设计表达式模板来防止编译器编译可能导致在运行时使用过期临时文件的代码?
(我试图解决这个问题没有成功,尝试在这里)
我了解执行以下操作:
auto&& x = Matrix1() + Matrix2() + Matrix3();
std::cout << x(2,3) << std::endl;
如果矩阵运算使用表达式模板(例如boost::ublas
),将导致静默运行时错误。
有没有什么方法可以设计表达式模板来防止编译器编译可能导致在运行时使用过期临时文件的代码?
(我试图解决这个问题没有成功,尝试在这里)
有没有什么方法可以设计表达式模板来防止编译器编译可能导致在运行时使用过期临时文件的代码?
没有。这实际上在 C++11 最终标准化之前就已经得到认可,但我不知道它是否曾引起委员会的注意。并不是说修复会很容易。auto
我想最简单的事情是类型上的标志,如果尝试推断它只会出错,但即使这样也会很复杂,因为decltype
也可以推断它,以及模板参数推断。所有这三个都以相同的方式定义,但您可能不希望后者失败。
只需适当地记录您的图书馆,并希望没有人试图以这种方式捕获它们。
据我了解,您的问题的根源是表达式模板临时可能具有指向其他临时对象的引用/指针。通过使用 auto&& 我们只延长表达式模板临时本身的生命周期,而不是它所引用的临时变量的生命周期。这样对吗?
例如,这是你的情况吗?
#include <iostream>
#include <deque>
#include <algorithm>
#include <utility>
#include <memory>
using namespace std;
deque<bool> pool;
class ExpressionTemp;
class Scalar
{
bool *alive;
friend class ExpressionTemp;
Scalar(const Scalar&);
Scalar &operator=(const Scalar&);
Scalar &operator=(Scalar&&);
public:
Scalar()
{
pool.push_back(true);
alive=&pool.back();
}
Scalar(Scalar &&rhs)
: alive(0)
{
swap(alive,rhs.alive);
}
~Scalar()
{
if(alive)
(*alive)=false;
}
};
class ExpressionTemp
{
bool *operand_alive;
public:
ExpressionTemp(const Scalar &s)
: operand_alive(s.alive)
{
}
void do_job()
{
if(*operand_alive)
cout << "captured operand is alive" << endl;
else
cout << "captured operand is DEAD!" << endl;
}
};
ExpressionTemp expression(const Scalar &s)
{
return {s};
}
int main()
{
{
expression(Scalar()).do_job(); // OK
}
{
Scalar lv;
auto &&rvref=expression(lv);
rvref.do_job(); // OK, lv is still alive
}
{
auto &&rvref=expression(Scalar());
rvref.do_job(); // referencing to dead temporary
}
return 0;
}
如果是,那么一种可能的解决方案是制作特殊类型的表达式模板临时文件,以保存从临时文件中移动的资源。
例如,检查这种方法(您可以定义 BUG_CASE 宏,以再次获取错误案例)。
//#define BUG_CASE
#include <iostream>
#include <deque>
#include <algorithm>
#include <utility>
#include <memory>
using namespace std;
deque<bool> pool;
class ExpressionTemp;
class Scalar
{
bool *alive;
friend class ExpressionTemp;
Scalar(const Scalar&);
Scalar &operator=(const Scalar&);
Scalar &operator=(Scalar&&);
public:
Scalar()
{
pool.push_back(true);
alive=&pool.back();
}
Scalar(Scalar &&rhs)
: alive(0)
{
swap(alive,rhs.alive);
}
~Scalar()
{
if(alive)
(*alive)=false;
}
};
class ExpressionTemp
{
#ifndef BUG_CASE
unique_ptr<Scalar> resource; // can be in separate type
#endif
bool *operand_alive;
public:
ExpressionTemp(const Scalar &s)
: operand_alive(s.alive)
{
}
#ifndef BUG_CASE
ExpressionTemp(Scalar &&s)
: resource(new Scalar(move(s))), operand_alive(resource->alive)
{
}
#endif
void do_job()
{
if(*operand_alive)
cout << "captured operand is alive" << endl;
else
cout << "captured operand is DEAD!" << endl;
}
};
template<typename T>
ExpressionTemp expression(T &&s)
{
return {forward<T>(s)};
}
int main()
{
{
expression(Scalar()).do_job(); // OK, Scalar is moved to temporary
}
{
Scalar lv;
auto &&rvref=expression(lv);
rvref.do_job(); // OK, lv is still alive
}
{
auto &&rvref=expression(Scalar());
rvref.do_job(); // OK, Scalar is moved into rvref
}
return 0;
}
您的运算符/函数重载可能会返回不同的类型,具体取决于 T&&/const T& 参数:
#include <iostream>
#include <ostream>
using namespace std;
int test(int&&)
{
return 1;
}
double test(const int&)
{
return 2.5;
};
int main()
{
int t;
cout << test(t) << endl;
cout << test(0) << endl;
return 0;
}
因此,当您的表达式模板临时没有从临时模板中移动资源时 - 它的大小不会受到影响。