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 和
上编译良好
- Debian clang 版本 3.5.0-9
- gcc 版本 4.9.1
- icpc 版本 15.0.1
和 Mac OS X 与
- gcc 版本 4.9.2
但没有
- clang-600.0.56
- 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] =]、double
和 long double
,以及 return 适当的 std::complex
实例化。
然后有一个来自 C99 库的全局函数 ::conj
,它采用 C99 double complex
和 return 相同。
据我所知,libstdc++ 尚未提供新的 C++11 conj
重载。未调用 conj
的 C++ 版本。然而,似乎 ::conj
以某种方式进入了 std
命名空间,并被调用。您传递的 double
通过添加零虚部隐式转换为 double complex
。 conj
否定零。通过丢弃虚部,结果 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); });
}
我知道我更喜欢哪一个。
如下代码
#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 和
上编译良好- Debian clang 版本 3.5.0-9
- gcc 版本 4.9.1
- icpc 版本 15.0.1
和 Mac OS X 与
- gcc 版本 4.9.2
但没有
- clang-600.0.56
- 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] =]、double
和 long double
,以及 return 适当的 std::complex
实例化。
然后有一个来自 C99 库的全局函数 ::conj
,它采用 C99 double complex
和 return 相同。
libstdc++ 尚未提供新的 C++11 conj
重载。未调用 conj
的 C++ 版本。然而,似乎 ::conj
以某种方式进入了 std
命名空间,并被调用。您传递的 double
通过添加零虚部隐式转换为 double complex
。 conj
否定零。通过丢弃虚部,结果 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); });
}
我知道我更喜欢哪一个。