8

Visual C++ 2017 compiles the following cleanly, calling the user-defined log:

// Source encoding: UTF-8 with BOM ∩
#include <algorithm>    // std::for_each
#include <iostream>
#include <math.h>       // ::(sin, cos, atan, ..., log)
#include <string>       // std::string

void log( std::string const& message )
{
    std::clog << "-- log entry: " << message << std::endl;
}

auto main()
    -> int
{
    auto const messages = { "Blah blah...", "Duh!", "Oki doki" };
    std::for_each( messages.begin(), messages.end(), log );         // C++03 style.
}

I think that's a compiler bug, since I designed the code to show how an identifier can be ambiguous due to name collision with the standard library.

Is it a compiler bug?


Supplemental info: MinGW g++ 7.2 issues several error messages. They're not exactly informative, 15 lines complaining about std::for_each, but evidently they're due to the name collision. Changing the name of log the code compiles nicely.


Update: Further checking indicates that it's clearly a compiler bug, because Visual C++ compiles the following (except when symbol D is defined):

#include <cmath>        // std::(sin, cos, atan, ..., log)
#include <string>       // std::string

namespace my{ void log( std::string const& ) {} }
using std::log;
using my::log;

auto main()
    -> int
#ifdef D
{ return !!log; }
#else
{ auto f = log; return f==my::log; }
#endif

Reported to Microsoft (the new MS bug reporting scheme is very buggy: it thought it was a good idea to word-wrap the code, then refused to let me upload source code file unless I gave it a ".txt" filename extension).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I'm not sure why `Visual C++` compiles it tbh. How can it tell between your `log` symbol and the one from `math.h`? – Galik Mar 21 '18 at 03:45
  • `g++ 7.3` on linux gives a sensible enough error "unresolved overloaded function type". Weird that `MinGW` behaves differently. – Galik Mar 21 '18 at 03:51
  • @Galik: It does mention that "unresolved" in the presented function signature for `std::for_each`. I suspect that all the errors also on linux were about `std::for_each`, and not one up-front message about `log`? – Cheers and hth. - Alf Mar 21 '18 at 03:54
  • @Cheersandhth.-Alf No, I literally get only one error message line `error: no matching function for call to ‘for_each(std::initializer_list::const_iterator, std::initializer_list::const_iterator, )’` followed by the program line and a little arrow pointing at the exact spot. – Galik Mar 21 '18 at 03:59
  • @galik: That's better than with MingW, but still it's an error about `std::for_each` call. Differences: with MinGW the arrow points at the closing parenthesis of the argument list, and the message is followed by more noise messages. – Cheers and hth. - Alf Mar 21 '18 at 04:02
  • MinGW-w64 (g++ 7.2.0) gives me the exact same error message output as [godbolt g++ 7.2.0](https://godbolt.org/g/Ke9qAh) – M.M Mar 21 '18 at 04:05
  • @M.M: Thanks, then there's no difference. The presented diagnostic is `no matching function for call to 'for_each'`. With umpteen support lines. – Cheers and hth. - Alf Mar 21 '18 at 04:07
  • @BenVoigt: Thanks, fixing. That was a typo (the question is based on the opposite view). Good defense, like the C++ standard: comments are not normative. :) – Cheers and hth. - Alf Mar 21 '18 at 04:34
  • 1
    Isn't everything in `math.h` allowed to be implemented as macros? If `log` is a macro that calls something else, there's no ambiguity. – Praetorian Mar 21 '18 at 04:41
  • @Praetorian: Interesting, could be explanation. I don't yet find that but C++14 says "Names which are defined as macros in C shall be defined as macros in the C ++ standard library, even if C grants license for implementation as functions. [Note: The names defined as macros in C include the following: assert, offsetof, setjmp, va_arg, va_end, and va_start. —end note ]" The note is both incomplete and non-normative, though. So could be far more macros, like all functions in math.h. – Cheers and hth. - Alf Mar 21 '18 at 04:45
  • I opened up VS2017 after posting that; `log` is not a macro, it's a function. So that doesn't explain it. As noted in one of the answers, the EDG frontend used for Intellisense does get it right and say it cannot determine which version of `log` to call. I'm going with compiler bug. – Praetorian Mar 21 '18 at 04:48
  • @Praetorian People here jump on the "compiler bug" train so quickly. Can you actually demonstrate that the standard requires a diagnostic in this case? – Arne Vogel Mar 21 '18 at 08:22
  • 1
    @ArneVogel: A demonstration would probably amount to a longer treatise. But a start is the section about implementation compliance, which defines *diagnosable rules* as all syntactic and semantic rules except those explicitly marked otherwise. Then it continues to note that a violation of a diagnosable rule requires at least one diagnostic message. That reduces the problem to finding the particular point in the standard where an ambiguous name reference is disallowed. You can be sure that it is, or else we have a defect of the standard at hand. – Cheers and hth. - Alf Mar 21 '18 at 08:44
  • Even if you had included `` rather than ``, wouldn't `std::log` be a candidate due to ADL? Yep, just tried (with GCC 8.0.1) and that's rejected, too. – Toby Speight Mar 21 '18 at 11:52
  • The macro possibility could be dealt with by doing `#undef log` and checking the behaviour is unchanged – M.M Mar 21 '18 at 12:04

2 Answers2

5

This is a compiler bug because the compiler should not be able to perform template argument deduction for the for_each call.

The only declaration of for_each that could match is defined as [alg.foreach]:

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f);

Template argument deduction applied on function parameter f needs the type of the function call argument log to proceed. But log is overloaded, and an overload set of functions does not have a type.

For example, this simpler code should not compile for the same reason:

#include <algorithm>    // std::for_each
#include <string>       // std::string

void log( std::string const& message );
void log();

auto main()
    -> int
{
    auto const messages = { "Blah blah...", "Duh!", "Oki doki" };
    std::for_each( messages.begin(), messages.end(), log );  //template argument deduction for template parameter Function failed.
}

It works in this version of MSVC because templates (used to be/) are implemented as a kind of macro, so log is passed as a name, and overload resolution is performed at the point where log is called in the body of for_each.


About the edit:

The expression !!log is equivalent to a call to bool operator(bool) there are no template argument deduction, the compiler just can not know which overload of log it can use to make the conversion to bool.

Inside declaration of the form auto x=y, the actual type of x is deduced using template argument deduction [dcl.type.auto.deduct]/4:

If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. [...]

So the behavior of MSVC is wrong but consistent.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • This sounds resonable. I will check later (maybe this evening). Thanks! – Cheers and hth. - Alf Mar 21 '18 at 10:05
  • I checked and there's no macro in sight. But I found out that that Visual C++ detects the ambiguity depending on the exact use of name `log`, i.e. it's inconsistent with itself. See update at the end of the question. – Cheers and hth. - Alf Mar 21 '18 at 12:46
  • @Cheersandhth.-Alf I made an edit. Your update showes that MSVC is consistent. When I said that template are implemented as a kind of macro I did not mean that template are macros. – Oliv Mar 21 '18 at 13:29
  • I'm sorry, that doesn't make sense to me. – Cheers and hth. - Alf Mar 21 '18 at 13:38
  • @Cheersandhth.-Alf For example MSVC used not to have two phase name look up [msvc article](https://blogs.msdn.microsoft.com/vcblog/2017/09/11/two-phase-name-lookup-support-comes-to-msvc/), and they still have many problem with template. Actualy comparing MSVC template and macro is not my idea, it comes from an article I read. Whatsoever, the MSVC team is aware of this non conformants behavior as you can read in this article they are working on making MSVC conformant. At least I don't think there is a need to fill a bug report. – Oliv Mar 21 '18 at 13:58
2

Defining your own ::log causes undefined behaviour (no diagnostic required).

From C++17 (N4659) [extern.names]/3:

Each name from the C standard library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace.

Link to related answer.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • This does not apply to my code, because **signature**: "Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++"linkage, or as a name of namespace scope in the global namespace." But thank you for that reference. It's a near duplicate (but not quite!). – Cheers and hth. - Alf Mar 21 '18 at 04:11
  • But does this let `VC++` off the hook? Even if you don't define your own `log()` function there are ambiguous overloads of the standard `log()` from `math.h` isn't there? – Galik Mar 21 '18 at 04:11
  • @Cheersandhth.-Alf You quote [extern.names]/4, however [extern.names]/3 also applies – M.M Mar 21 '18 at 04:14
  • 1
    The text now quoted does not apply to my code, because **`extern "C"` linkage**. But again, thanks. I'm sure I've known this, but I've forgotten and am re-learning. ;-) – Cheers and hth. - Alf Mar 21 '18 at 04:14
  • @Cheersandhth.-Alf Anyway it is reserved (for whatever use), then the last rule in the linked answer applies. – xskxzr Mar 21 '18 at 11:57
  • @xskxzr the argument being made is that declaring the name as a function with C++ linkage (as in the code in the question) does not count as declaring it in the reserved context , because that context is only "extern C function in global namespace". My answer might be wrong but I'll leave it up for now to get further feedback – M.M Mar 21 '18 at 12:00