13

When I run this code on ideone.com, it prints (2,3):

#include <iostream>
#include <complex>

int main() {
    std::complex<double> val = 2 + 3i;
    std::cout << val << std::endl;
    return 0;
}

But when I use clang on macOS 10.11.6, I get no errors or warnings, but the output is (2,0):

$ clang --version
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.6.0

$ clang -lc++ test.cpp && ./a.out
(2,0)

What happened to the imaginary part? Am I doing something wrong?

jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • Did you try [this](https://ideone.com/RbQDU6) with clang? – πάντα ῥεῖ Aug 27 '16 at 06:57
  • Just did. Same result. – jtbandes Aug 27 '16 at 06:58
  • Shouldn't the Clang compile command be `clang++ -std=c++14`? – Kyle Strand Aug 27 '16 at 06:59
  • Interesting. `-std=c++14` triggers the error `no matching literal operator for call to 'operator""i' ...`. Why on earth would it compile without this? – jtbandes Aug 27 '16 at 07:01
  • @jtbandes user-defined literals as a *language* feature were part of C++11. Literal operators for string, chrono, complex were put into the *library* for C++14. So the C++11 compiler knew `3i` was a user-defined literal but was unable to find a `operator""i(unsigned long long n)` in any library. – emsr Aug 27 '16 at 20:55
  • @emsr that's not exactly the reason. It didn't know `3i` was a user-defined literal, but it treated it as a `_Complex` value from a GNU extension. See my comments on the answer below. – jtbandes Aug 27 '16 at 21:03
  • @jtbandes Ah. That's a nasty cross-version gotcha that I've been hit by. Arithmetic will work for C++14 but not C++11. – emsr Aug 27 '16 at 21:15

1 Answers1

10

I believe for this first example the compiler is using a GNU extension:

-fext-numeric-literals (C++ and Objective-C++ only)

Accept imaginary, fixed-point, or machine-defined literal number suffixes as GNU extensions. When this option is turned off these suffixes are treated as C++11 user-defined literal numeric suffixes. This is on by default for all pre-C++11 dialects and all GNU dialects: -std=c++98, -std=gnu++98, -std=gnu++11, -std=gnu++14. This option is off by default for ISO C++11 onwards (-std=c++11, ...).

When I run it with clang I get (are you using -Wall -pedantic? :)):

warning: imaginary constants are a GNU extension [-Wgnu-imaginary-constant]

Either way, your code is not standard compliant. To use C++14 literals make the code:

#include <iostream>
#include <complex>
using namespace std::complex_literals;
int main() {
    std::complex<double> val = 2.0 + 3i;
    std::cout << val << std::endl;
    return 0;
}

From the documentation:

These operators are declared in the namespace std::literals::complex_literals, where both literals and complex_literals are inline namespaces. Access to these operators can be gained with using namespace std::literals, using namespace std::complex_literals, and using namespace std::literals::complex_literals.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • Ah, I should have thought to fiddle with warning flags... indeed, enabling -Weverything gives me `warning: imaginary constants are a GNU extension`, and even more importantly `warning: implicit conversion discards imaginary component`. It's frustrating that this warning wouldn't be on by default... – jtbandes Aug 27 '16 at 07:03
  • 1
    @jtbandes: I see, the imaginary part is getting thrown out by the implicit conversion to int. Lesson learned here is to `always compile at the highest warning level possible`. This is true for all compiled languages. – Jesse Good Aug 27 '16 at 07:15