C++ Error Messages of Different Compilers

The error messages of compilers are in general a bit hard to understand. This is even worse with C++ because it allows for complex meta-programming and various other things.

Take this simple test program:

#include <iostream>
#include <random>

int main() {
    std::mt19937 engine();
    std::normal_distribution<double> const dist(0, 1);

    std::cout << dist(engine) << std::endl;
}

This should just output a single random number between zero and one. It does not look wrong right away. When you compile that with GCC, you get the following:

g++ -Wall -Wpedantic test.cpp
test.cpp: In function 'int main()':
test.cpp:8:29: error: no match for call to '(const std::normal_distribution<double>) (std::mt19937 (&)())'
     std::cout << dist(engine) << std::endl;
                             ^
In file included from /usr/include/c++/6.2.1/random:49:0,
                 from test.cpp:2:
/usr/include/c++/6.2.1/bits/random.h:2030:2: note: candidate: std::normal_distribution<_RealType>::result_type std::normal_distribution<_RealType>::operator()(_UniformRandomNumberGenerator&) [with _UniformRandomNumberGenerator = std::mersenne_twister_engine<long unsigned int, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>(); _RealType = double; std::normal_distribution<_RealType>::result_type = double] <near match>
  operator()(_UniformRandomNumberGenerator& __urng)
  ^~~~~~~~
/usr/include/c++/6.2.1/bits/random.h:2030:2: note:   passing 'const std::normal_distribution<double>*' as 'this' argument discards qualifiers
/usr/include/c++/6.2.1/bits/random.h:2035:2: note: candidate: template<class _UniformRandomNumberGenerator> std::normal_distribution<_RealType>::result_type std::normal_distribution<_RealType>::operator()(_UniformRandomNumberGenerator&, const std::normal_distribution<_RealType>::param_type&) [with _UniformRandomNumberGenerator = _UniformRandomNumberGenerator; _RealType = double]
  operator()(_UniformRandomNumberGenerator& __urng,
  ^~~~~~~~
/usr/include/c++/6.2.1/bits/random.h:2035:2: note:   template argument deduction/substitution failed:
test.cpp:8:29: note:   candidate expects 2 arguments, 1 provided
     std::cout << dist(engine) << std::endl;
                             ^

Do you see what is wrong here? I did not on the first glance. Part of the problem is that I have done a lot of Python before starting this C++ project.

There are two things wrong here:

  1. The declaration of engine is meant to be a default-constructed variable. The way I wrote it (with the empty parentheses), it is actually the declaration of a function! So engine is now a function that does not take any arguments and returns a Mersenne Twister instance. Therefore the argument to dist is of the wrong type.

  2. Marking dist as a constant does not work, it must be mutable in order to generate random numbers from it.

Then I compiled the same with with Clang:

clang++ -Wall -Wpedantic --std=c++11 test.cpp
test.cpp:5:24: warning: empty parentheses interpreted as a function declaration [-Wvexing-parse]
    std::mt19937 engine();
                       ^~
test.cpp:5:24: note: remove parentheses to declare a variable
    std::mt19937 engine();
                       ^~
test.cpp:8:18: error: no matching function for call to object of type 'const std::normal_distribution<double>'
    std::cout << dist(engine) << std::endl;
                 ^~~~
/usr/bin/../lib/gcc/x86_64-redhat-linux/6.2.1/../../../../include/c++/6.2.1/bits/random.h:2030:2: note: candidate function not viable: 'this' argument has type 'const std::normal_distribution<double>', but method is not marked const
        operator()(_UniformRandomNumberGenerator& __urng)
        ^
/usr/bin/../lib/gcc/x86_64-redhat-linux/6.2.1/../../../../include/c++/6.2.1/bits/random.h:2035:2: note: candidate function template not viable: requires 2 arguments, but 1 was provided
        operator()(_UniformRandomNumberGenerator& __urng,
        ^
1 warning and 1 error generated.

Here the very first warning tells me that I probably don't mean what I wrote. And indeed, I have used syntax from Python or Java. Removing those extra parentheses gets me the following errors from GCC:

test.cpp: In function 'int main()':
test.cpp:8:29: error: no match for call to '(const std::normal_distribution<double>) (std::mt19937&)'
     std::cout << dist(engine) << std::endl;
                             ^
In file included from /usr/include/c++/6.2.1/random:49:0,
                 from test.cpp:2:
/usr/include/c++/6.2.1/bits/random.h:2030:2: note: candidate: std::normal_distribution<_RealType>::result_type std::normal_distribution<_RealType>::operator()(_UniformRandomNumberGenerator&) [with _UniformRandomNumberGenerator = std::mersenne_twister_engine<long unsigned int, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>; _RealType = double; std::normal_distribution<_RealType>::result_type = double] <near match>
  operator()(_UniformRandomNumberGenerator& __urng)
  ^~~~~~~~
/usr/include/c++/6.2.1/bits/random.h:2030:2: note:   passing 'const std::normal_distribution<double>*' as 'this' argument discards qualifiers
/usr/include/c++/6.2.1/bits/random.h:2035:2: note: candidate: template<class _UniformRandomNumberGenerator> std::normal_distribution<_RealType>::result_type std::normal_distribution<_RealType>::operator()(_UniformRandomNumberGenerator&, const std::normal_distribution<_RealType>::param_type&) [with _UniformRandomNumberGenerator = _UniformRandomNumberGenerator; _RealType = double]
  operator()(_UniformRandomNumberGenerator& __urng,
  ^~~~~~~~
/usr/include/c++/6.2.1/bits/random.h:2035:2: note:   template argument deduction/substitution failed:
test.cpp:8:29: note:   candidate expects 2 arguments, 1 provided
     std::cout << dist(engine) << std::endl;
                             ^

The problem is the const. But I would not read that from the errors if I did not know that. In some way it is there:

passing const std::normal_distribution<double>* as this argument discards qualifiers

Then Clang on the other hand tells me this:

test.cpp:8:18: error: no matching function for call to object of type 'const std::normal_distribution<double>'
    std::cout << dist(engine) << std::endl;
                 ^~~~
/usr/bin/../lib/gcc/x86_64-redhat-linux/6.2.1/../../../../include/c++/6.2.1/bits/random.h:2030:2: note: candidate function not viable: 'this' argument has type 'const std::normal_distribution<double>', but method is not marked const
        operator()(_UniformRandomNumberGenerator& __urng)
        ^
/usr/bin/../lib/gcc/x86_64-redhat-linux/6.2.1/../../../../include/c++/6.2.1/bits/random.h:2035:2: note: candidate function template not viable: requires 2 arguments, but 1 was provided
        operator()(_UniformRandomNumberGenerator& __urng,
        ^
1 error generated.

There I find it quite clear that the const is the problem and not something else:

candidate function not viable: this argument has type const std::normal_distribution<double>, but method is not marked const

The error messages by Clang are easier to read for me. It takes less time for me to figure out the errors. So currently I perform my debug builds with Clang to have an easier time finding my errors. When for production I use GCC as it comes with OpenMP support such that I can use all the cores in my machine.

This is done with the following snippet in my CMake configuration:

set(clang_path "/usr/bin/clang++")

if(CMAKE_BUILD_TYPE MATCHES debug AND EXISTS "${clang_path}")
    set(CMAKE_CXX_COMPILER "${clang_path}")
else()
    set(CMAKE_CXX_COMPILER "/usr/bin/g++")
endif()

message("Using ${CMAKE_CXX_COMPILER}")

Then call CMake with

cmake ../source -DCMAKE_RELEASE_TYPE=Release

or

cmake ../source -DCMAKE_RELEASE_TYPE=Debug

and you can get different compilers for both build types. This also makes sure that your code compiles with other compiles since you cannot use and GCC extensions (well, OpenMP works as Clang ignores the #pragma omp).

There are probably other examples where the GCC message is easier to read than the Clang message. Both compilers are good software, I don't want to imply anything like that. It is just that sometimes the Clang messages are more helpful. In some cases I found it very handy to read both error messages as they give more perspective. Using the above CMake snippet this is really easy to implement in practice.