C++ 的 GMP:`auto` 带来更多麻烦

问题描述 投票:0回答:1

这里有更多关于 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
返回类型”,但我更喜欢使用它 - 这可以简化修改。

系统环境:

  • 操作系统:Ubuntu 22.04.4 LTS
  • 编译器:g++ (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
  • 编译器/链接器标志:-O3 -Wall -std=c++20 -lgmpxx -lgmp
  • GMP版本:6.2.1
c++ gmp
1个回答
0
投票

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
,否则就没有什么可以触发从表达式模板到数字的隐式转换。

© www.soinside.com 2019 - 2024. All rights reserved.