The following code
#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;
}
compiles fine on Linux with
and on Mac OS X with
but not with
except if the macro OK
is defined. I don't know which are the faulty compilers, could someone let me know ? Thanks.
PS: here is the error
10:48: error: assigning to 'double' from incompatible type 'complex<double>'
std::for_each(in, in + m, [](K& z) { z = std::conj(z); });
The difference is that on Linux, you're using libstdc++ and glibc, and on MacOS you're using libc++ and whatever CRT MacOS uses.
The MacOS version is correct. (Also, your workaround is completely broken and insanely dangerous.)
Here's what I think happens.
There are multiple overloads of conj
in the environment. C++98 brings in a single template, which takes a std::complex<F>
and returns the same type. Because this template needs F
to be deduced, it doesn't work when calling conj
with a simple floating point number, so C++11 added overloads of conj
which take float
, double
and long double
, and return the appropriate std::complex
instantiation.
Then there's a global function from the C99 library, ::conj
, which takes a C99 double complex
and returns the same.
libstdc++ doesn't yet provide the new C++11 conj
overloads, as far as I can see. The C++ version of conj
isn't called. It appears, however, that somehow ::conj
found its way into the std
namespace, and gets called. The double
you pass is implicitly converted to a double complex
by adding a zero imaginary part. conj
negates that zero. The result double complex
is implicitly converted back to a double
by discarding the imaginary component. (Yes, that's an implicit conversion in C99. No, I don't know what they were thinking.) The result can be assigned to z
.
libc++ provides the new overloads. The one taking a double
is chosen. It returns a std::complex<double>
. This class has no implicit conversion to double
, so the assignment to z
gives you an error.
The bottom line is this: your code makes absolutely no sense. A vector<double>
isn't a vector<complex<double>>
and shouldn't be treated as one. Calling conj
on double
doesn't make sense. Either it doesn't compile, or it's a no-op. (libc++'s conj(double)
is in fact implemented by simply constructing a complex<double>
with a zero imaginary part.) And wildly reinterpret_cast
ing your way around compile errors is horrible.