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

时间:2015-01-23 15:47:47

标签: c++11 g++ complex-numbers clang++ icc

以下代码

#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 version 4.9.1
  3. icpc version 15.0.1
  4. 和Mac OS X上的

    1. gcc version 4.9.2
    2. 但不是

      1. 铛-600.0.56
      2. icpc version 15.0.1
      3. 除非定义了宏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); });
        

2 个答案:

答案 0 :(得分:5)

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

MacOS版本是正确的。 (此外,您的解决方法完全被破坏并且非常危险。)

这就是我认为发生的事情。

环境中有多个conj重载。 C ++ 98引入了一个模板,它采用std::complex<F>并返回相同的类型。由于此模板需要推导出F,因此在使用简单的浮点数调用conj时它不起作用,因此C ++ 11添加了conj的重载,其中{{1} },floatdouble,并返回相应的long double实例。

然后是来自C99库std::complex的全局函数,它接受C99 ::conj并返回相同的内容。

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

libc ++提供了新的重载。选择z的人。它返回double。此类没有隐式转换为std::complex<double>,因此对double的分配会给您一个错误。

最重要的是:你的代码完全没有意义。 z不是vector<double>,不应视为一个。在vector<complex<double>>上拨打conj没有意义。要么它不编译,要么它是无操作。 (libc ++的double实际上是通过简单地构造一个虚部为零的conj(double)来实现的。)然而,complex<double>编译错误非常可怕。

答案 1 :(得分:3)

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); });
}

我知道我更喜欢哪一个。