Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does <algorithm> include <cmath>?

The following program compiles correctly:

#include <algorithm>

int main(int argc, char *argv[]) {

    return int(log(23.f));

}

(under g++ 4.9.2 with the flag -std=c++11)

The code uses the function log, which is defined on <cmath>. However, it does not include the header <cmath>, only the header <algorithm>. Why is it that g++ doesn't give any warnings, and compiles the code correctly?

like image 450
fonini Avatar asked Apr 05 '15 05:04

fonini


3 Answers

According to the standard, some headers do include others. As an example, <cinttypes> includes <cstdint>. See the Includes section here. With respect to <algorithm>, there is no such statement as to which other headers it should include (see here). So, the conclusion is, <algorithm> is not required to include <cmath>, and your example code is not portable. It may fail to compile on other C++ implementations.

like image 159
Lingxi Avatar answered Oct 19 '22 08:10

Lingxi


In the C++11 standard, [res.on.headers]/1 states that

A C++ header may include other C++ headers. A C++ header shall provide the declarations and definitions that appear in its synopsis. A C++ header shown in its synopsis as including other C++ headers shall provide the declarations and definitions that appear in the synopses of those other headers.

Now consider [algorithms.general]/2:

Header <algorithm> synopsis

#include <initializer_list>

namespace std {
  // ......

<cmath> isn't listed and clearly not included in <initializer_list>. Thus your program is not guaranteed to compile on a standard-conforming implementation. One should never rely on "implicit inclusion" - the general guideline is to include every header from which an entity is used.
Exceptions are e.g. <iostream> including <ostream>, which is guaranteed since C++11.

like image 29
Columbo Avatar answered Oct 19 '22 08:10

Columbo


To answer your question:

Why is it that g++ doesn't give any warnings, and compiles the code correctly?

Because C++ implementations aren't required to and it's actually quite difficult to implement this warning given the way #include works. Attempts have been made, but there are problems that haven't been entirely addressed.


Moving to a different model can enable this kind of checking. However, in the interests of backwards compatibility and allowing the easiest possible transition the 'modularizations' of the standard library I've used happen to explicitly allow code that previously depended on indirect includes to continue to work.

You can see this, for example, in libc++'s module map; Those export * lines declare "any modules imported by this module are also exported." Which is to say, a module std.algorithm that imports a module std.cmath also exports, so anyone that imports std.algorithm also gets access to std.cmath.

For new code it would be very nice if these 'legacy exports' could be turned off, but for pre-existing large projects it is very nice to be able to just flip on -fmodules and have the project work with no changes.


Using clang's implementation of modules with libc++, and modifying the module map file to remove the non-portable, indirect include behavior, clang reports such errors like:

main.cpp:5:16: error: declaration of 'log' must be imported from module 'Darwin.C.math' before it is required

return int(log(23.f));
           ^  

/usr/include/math.h:387:15: note: previous declaration is here

extern double log(double);  
              ^  

1 error generated.

libc++ <algorithm> doesn't include <cmath>, so I used <random> instead. Otherwise the source that produced the above is the same as what you show.

like image 45
bames53 Avatar answered Oct 19 '22 07:10

bames53