C++中的复数?

13

下面的代码在C中可以编译和运行(至少按照'gcc-std = gnu99'的说法),但在C++下无法编译,报错为“第5行:错误:无法将'double'转换为'双倍复数'进行初始化”。有人知道原因吗?

#include "/usr/include/complex.h"
#include <stdio.h>

int main(int argc, char * argv[]) {
  double complex a = 3;  // ERROR ON THIS LINE
  printf("%lf\n", creal(a));
  return 0;
}

我意识到在C++中有另一种处理复数的方式,但是我必须使用C语言中的复数来处理C++,因为这就是我得到的旧代码所采用的方法。如果您能帮忙,感谢!


5
但是,你到底为什么不想使用std::complex<>呢? - ildjarn
3
对于C语言来说,-std=gnu99并不是你真正想要的编译选项。它是“带有GNU认为是好主意的东西的C语言”,但这是否是好主意存在不同解释。如果你想用C语言,建议使用-std=c99编译选项。 - R. Martinho Fernandes
1
ildjarn: 不幸的是,我正在使用C++中的C99复数库。这似乎不是一个明智的开始项目的方式,但这是我必须处理的代码。Martinho: 那是真的。我应该用-std=c99进行测试。 - iloveponies
3个回答

19

C++编译器可以选择支持_Complex关键字作为扩展(一些编译器确实这样做了),但这不是可移植的。如果您想要一个可移植的C++解决方案,不幸的是,您需要使用C++ std::complex模板。

好消息是,C++ std::complex数值保证与C复数兼容(指指针始终可以转换为另一个指针,并且会发生正确的事情),这意味着如果您需要与期望C复数值的C库进行交互,您不会遇到任何问题。

C11:

每个复数类型具有与包含相应实数类型的两个元素的数组类型完全相同的表示和对齐要求;第一个元素等于复数的实部,第二个元素等于虚部。

C++11:

如果z是类型为cv std::complex<T>的左值表达式,则:

— 表达式reinterpret_cast<cv T(&)[2]>(z)应该是良好形式的,

reinterpret_cast<cv T(&)[2]>(z)[0]应该指定z的实部,

reinterpret_cast<cv T(&)[2]>(z)[1]应该指定z的虚部。


那个保证在哪里?(“C++ std::complex numbers are guaranteed to be compatible with C99 complex numbers”)下面的回答(https://dev59.com/V2kv5IYBdhLWcg3wghIh#10540288)引用了一个非规范的来源(维基百科),它说相反的话。 - Jeff Hammond
2
@Jeff:维基百科链接实际上并没有反驳我的说法;它们是不同的类型,但保证具有相同的内存布局和对齐要求。我已经在我的答案中添加了适当的参考资料。 - Stephen Canon
这里的章节和节是“29.5复数[complex.numbers]”注4:如果z是类型为cv complex<T>的lvalue表达式,则表达式reinterpret_cast<cv T(&)[2]>(z)reinterpret_cast<cv T(&)[2]>(z)[0]z的实部,reinterpret_cast<cv T(&)[2]>(z)[1]z的虚部。 如果a是类型为cv complex<T>*的表达式,并且对于整数表达式i,表达式a[i]是定义良好的,则reinterpret_cast<cv T*>(a)[2*i]应指定a[i]的实部,reinterpret_cast<cv T*>(a)[2*i + 1]应指定a[i]的虚部。 - emsr

10

使用C语言的关键字_Complex来表示复数。而在C++中,使用complex作为模板类。

我不确定creal在哪里,否则我就将其注释取消了。

#include <complex.h>
#include <cstdio>

int main(int argc, char * argv[]) {
  double _Complex a = 3.0 + 0.0I;  // DECLARATION WORKS NOW - NOTE ASSIGNMENT MUST HAVE IMAG PART
  //printf("%lf\n", creal(a));
  return 0;
}

这个在gcc中可行(我使用g++编译)。我收到了有关弃用.h头文件的警告。

这里是一个电子邮件链接,显示C++和C之间的复数非标准兼容性。 C++11要求C++ complex与C _Complex具有布局兼容性。

我目前正在研究C ++中的creal等内容。 我在标准中没有找到任何东西。 由于似乎有一些努力在C ++和C之间提供一些源代码兼容性,因此creal,cpow等可能是TR2库提案的不错补充。


非常感谢!!!根据你所说的,我成功得到了解决方案。然而,Stack Overflow尚不允许我发布这个答案,但是你需要将虚部加入到赋值操作中。 - iloveponies
截至本评论撰写的日期/时间,上述代码在较新版本的g++ 4.8.x中会产生编译器错误,即“无法找到数字文字运算符'operator"" I'”。很可能是因为较新版本的g++更符合标准,因此会在此语法上出现错误。我还尝试了_Complex_I,但在g++上也无法编译。 - Trevor Boyd Smith
1
如果您不尝试初始化或设置变量,那么您可以编译代码。这意味着您在使用C++编译器时使用C样式复杂数据类型的能力是有限的。如果您想在C++中使用复杂的数字处理,我认为推荐的解决方案是使用std::complex<>。如果您被迫使用C代码,则必须使用C99的复杂类型和由<complex.h>提供的功能。换句话说,只需阅读@StephenCanon的答案:)(即使用std::complex)。 - Trevor Boyd Smith
@TrevorBoydSmith 我同意std::complex是远远优于其他的选择。OP担心使用C++编译C代码时会出现问题。可以通过定义大写'I'字面量运算符来恢复初始化。(你会收到有关用户空间字面量操作符中缺少下划线的警告。) 还要注意,std::complex<double> z; static_cast<double(&)[2]>(z) 是std::complex与其他语言的复数重叠的要求。丑陋但却是真实的。 - emsr
我在下面@StephenCanon的回答中添加了正确的标准参考和语法作为注释。 - emsr

9

C和C++的兼容性

C99的一些扩展在C++中不受支持或与C++功能冲突,例如可变参数宏、复合字面量、指定初始化器、可变长度数组和本地复数类型。C99中定义的long long int数据类型和restrict限定词未包含在当前的C++标准中,但一些编译器(如GNU编译器集合[4])提供它们作为扩展。long long数据类型以及可变模板,可以实现一些可变参数宏的功能,都包含在新的C++标准C++11中。另一方面,C99通过纳入C++特性(如//注释和混合声明和代码)来减少其他不兼容性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接