这里有更多关于 GMP C++ 类接口 的奇怪行为,与 C++
auto
关键字相关。我的上一个问题也是关于这个关键字的,它的删除解决了我的问题。然而,这是编译问题,但现在是运行时问题。
下面的简单测试编译正常,但出现 coredumps。
#include <iostream>
#include <gmpxx.h>
auto test()
{
return mpz_class(2) * 2;
}
int main()
{
std::cout << test() << std::endl;
}
如果将关键字
auto
替换为mpz_class
,则测试将正确运行。另外 - 如果省略编译标志 -O3
,那么测试将运行,但会打印错误的数字,该数字会随每次执行而变化(这通常在执行依赖于随机未初始化数据时发生)。
看起来
gmpxx.h
标头包含 something,这使得 C++ 编译器在这种情况下生成错误的代码。如何中和这个东西?
一个可能的答案是“不要使用
auto
返回类型”,但我更喜欢使用它 - 这可以简化修改。
系统环境:
GMP C++ 绑定的文档非常清楚:避免对 GMP 表达式使用
auto
。 (事实上,即使是普通的模板也会出现问题)。
问题是GMP依赖于“表达模板”技术。对于
mpz_class a, b
,这意味着 a + b
本身不是 mpz_class
,而是代表 a
和 b
之和的某种其他类型的对象,而无需实际计算它。这样的对象通常会通过引用来引用 a
和 b
。该对象可以隐式转换为 mpz_class
。这个想法是,这种技术允许根据生成其操作数的函数来专门化函数。例如。这是一个玩具示例:
struct my_num { double num; };
template<typename... Ts>
struct my_num_sum { // expression template representing sums
Ts&... summands;
operator my_num() {
// pretend that there is a more efficient method to add together multiple values of my_num at once than just repeatedly using +
return my_num((0.0 + ... + my_num(summands).num));
}
};
auto operator+(my_num &a, my_num &b) { return my_num_sum(a, b); }
// this specializes the addition operator to do something different (i.e. more efficient) when one of the operands is itself a sum
template<typename... Ts, typename U>
auto operator+(my_num_sum<Ts...> as, U &b) {
return my_num_sum(as.summands..., b);
}
// th
在此示例中,对于
my_num a, b, c
,表达式 a + b + c
不是 my_num
,而是 my_num_sum<my_num, my_num, my_num>
。 my_num d = a + b + c
行一次性将 a
、b
和 c
相加(一次调用 my_num_sum::operator my_num()
),这比将 a
和 b
添加到中间对象中更高效然后添加c
。 (这对于 double
来说并非如此,但对于向量等较大类型确实有意义)。
你看现在有一个很大的危险:用户函数很容易产生悬空引用:
auto oops(my_num a, my_num b) { return a + b; }
// returns a my_num_sum containing dangling references to parameters!
但是如果您要求实际数字,问题就会消失,因为现在使用从表达式模板到实际数据类型的隐式转换。
my_num fine(my_num a, my_num b) { return a + b; }
如果您确实坚持使用
auto
返回类型,那很好,但现在您仍然需要在不希望表达式模板泄漏的地方向 my_num
添加强制转换。
auto okay(my_num a, my_num b) { return my_num(a + b); }
这正是 GMP 文档针对您的问题建议的两个修复方法。编写以下任一内容以正确定义
test
:
mpz_class test() { return mpz_class(2) * 2; }
auto test() { return mpz_class(mpz_class(2) * 2); }
你必须在某个地方说
mpz_class
,否则就没有什么可以触发从表达式模板到数字的隐式转换。