不是模棱两可的标识符

Not ambiguous identifier

本文关键字:标识符 模棱两可      更新时间:2023-10-16

>Visual C++ 2017 通过调用用户定义的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.
}

我认为这是一个编译器错误,因为我设计代码是为了显示标识符如何由于与标准库的名称冲突而变得不明确。

是编译器错误吗?


补充信息:MinGW g++ 7.2 发布了几条错误消息。它们的信息并不完全,有15行抱怨std::for_each,但显然是由于名称冲突。更改代码log的名称可以很好地编译。


更新:进一步检查表明这显然是一个编译器错误,因为 Visual C++ 编译以下内容(定义符号D时除外):

#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

报告给Microsoft(新的 MS 错误报告方案非常错误:它认为对代码进行文字换行是个好主意,然后拒绝让我上传源代码文件,除非我给它一个".txt"文件扩展名)。

这是一个编译器错误,因为编译器应该无法对for_each调用执行模板参数推断。

唯一可以匹配的for_each声明定义为 [alg.foreach]:

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

应用于函数参数的模板参数推导f需要函数调用参数的类型log才能继续。但是日志是重载的,并且重载的函数集没有类型。

例如,出于同样的原因,不应编译此更简单的代码:

#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.
}

它适用于此版本的 MSVC,因为模板(以前是/)是作为一种宏实现的,因此log作为名称传递,并且在for_each正文中调用log的位置执行重载解析。


关于编辑:

表达式!!log相当于调用bool operator(bool)没有模板参数推导,编译器只是不知道可以使用哪个重载log进行bool转换。

在形式auto x=y的内部声明中,使用模板参数推导 [dcl.type.auto.deduct]/4 推导出x的实际类型:

如果占位符是自动类型说明符,则使用模板参数推导规则确定推导的类型 T' 替换 T。[...]

因此,MSVC的行为是错误的,但始终如一。

定义自己的::log会导致未定义的行为(无需诊断)。

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

使用外部链接声明的 C 标准库中的每个名称都保留给实现,以便在命名空间 std 和全局命名空间中用作具有外部"C"链接的名称。

链接到相关答案。