C++11 的不同编译器行为

Different compiler behavior with C++11

如下代码

#include <vector>
#include <complex>
#include <algorithm>

template<class K>
inline void conjVec(int m, K* const in) {
    static_assert(std::is_same<K, double>::value || std::is_same<K, std::complex<double>>::value, "");
    if(!std::is_same<typename std::remove_pointer<K>::type, double>::value)
#ifndef OK
        std::for_each(in, in + m, [](K& z) { z = std::conj(z); });
#else
        std::for_each(reinterpret_cast<std::complex<double>*>(in), reinterpret_cast<std::complex<double>*>(in) + m, [](std::complex<double>& z) { z = std::conj(z); });
#endif
}

int main(int argc, char* argv[]) {
    std::vector<double> nums;
    nums.emplace_back(1.0);
    conjVec(nums.size(), nums.data());
    return 0;
}

在 Linux 和

上编译良好
  1. Debian clang 版本 3.5.0-9
  2. gcc 版本 4.9.1
  3. icpc 版本 15.0.1

和 Mac OS X 与

  1. gcc 版本 4.9.2

但没有

  1. clang-600.0.56
  2. icpc 版本 15.0.1

除非定义了宏OK。我不知道哪些编译器有问题,有人可以告诉我吗?谢谢

PS: 这是错误

10:48: error: assigning to 'double' from incompatible type 'complex<double>'
        std::for_each(in, in + m, [](K& z) { z = std::conj(z); });

不同之处在于,在 Linux 上,您使用的是 libstdc++ 和 glibc,而在 MacOS 上,您使用的是 libc++ 以及 MacOS 使用的任何 CRT。

MacOS 版本正确。 (此外,您的解决方法已完全失效且极其危险。)

这是我认为会发生的情况。

环境中 conj 有多个重载。 C++98 引入了一个模板,它采用相同类型的 std::complex<F> 和 return。因为这个模板需要推导 F,当用简单的浮点数调用 conj 时它不起作用,所以 C++11 添加了 conj 的重载,它需要 [=15] =]、doublelong double,以及 return 适当的 std::complex 实例化。

然后有一个来自 C99 库的全局函数 ::conj,它采用 C99 double complex 和 return 相同。

据我所知,

libstdc++ 尚未提供新的 C++11 conj 重载。未调用 conj 的 C++ 版本。然而,似乎 ::conj 以某种方式进入了 std 命名空间,并被调用。您传递的 double 通过添加零虚部隐式转换为 double complexconj 否定零。通过丢弃虚部,结果 double complex 隐式转换回 double。 (是的,那是C99中的隐式转换,不,我不知道他们在想什么。)结果可以赋值给z

libc++ 提供了新的重载。选择 double 的那个。它 return 是 std::complex<double>。此 class 没有隐式转换为 double,因此对 z 的赋值给您一个错误。

底线是:您的代码完全没有意义。 vector<double> 不是 vector<complex<double>>,不应将其视为一个。在 double 上调用 conj 没有意义。它要么不编译,要么是空操作。 (libc++ 的 conj(double) 实际上是通过简单地构造一个虚部为零的 complex<double> 来实现的。)疯狂地 reinterpret_cast 绕过编译错误是可怕的。

Sebastian Redl 的回答解释了为什么您的代码没有用 libc++ 编译但用 libstdc++ 编译。 if 不是某些语言中存在的 static if;即使 if 分支中的代码 100% 无效,它仍然必须是有效代码。

无论如何,这对我来说感觉像是大量不必要的复杂性。并非所有东西都必须是模板。特别是当您的模板只能用于两种类型时,并且当与这两种类型中的一种一起使用时,它是无操作的。

比较:

template<class K>
inline void conjVec(int m, K* const in) {
    static_assert(std::is_same<K, double>::value || std::is_same<K, std::complex<double>>::value, "");
    if(!std::is_same<K, double>::value)
        std::for_each(reinterpret_cast<std::complex<double>*>(in), reinterpret_cast<std::complex<double>*>(in) + m, [](std::complex<double>& z) { z = std::conj(z); });
}

与:

inline void conjVec(int m, double* const in) {}
inline void conjVec(int m, std::complex<double>* const in) {
    std::for_each(in, in + m, [](std::complex<double>& z) { z = std::conj(z); });
}

我知道我更喜欢哪一个。