23

我被下面的代码严重咬伤了,我在上面浪费了很多小时的宝贵时间。

#include<string>

int next(std::string param){
    return 0;
}

void foo(){
    next(std::string{ "abc" });
}

这会产生以下编译器错误(在 Visual Studio 2013 上):

1>------ Build started: Project: sandbox, Configuration: Debug Win32 ------
1>  test.cpp
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2039: 'iterator_category' : is not a member of 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'
1>          c:\users\ray\dropbox\programming\c++\sandbox\test.cpp(8) : see reference to class template instantiation 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>' being compiled
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2146: syntax error : missing ';' before identifier 'iterator_category'
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2602: 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category' is not a member of a base class of 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371) : see declaration of 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category'
1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility(371): error C2868: 'std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category' : illegal syntax for using-declaration; expected qualified-name
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

后来我发现,如果我将函数名称从更改为其他名称next(),一切都很好。对我来说,这表明存在名称冲突,特别是 name next。我觉得这很奇怪,因为我没有使用类似using namespace std. 据我所知,next不是内置的 C++ 关键字(是吗?)。我抬头看了看next 这里,但std::next正如我所说,我没有using namespace std。那么这场冲突是如何发生的呢?我如何防止将来发生类似的事情?还有哪些其他名称可能会导致这样的冲突?

4

4 回答 4

26
于 2015-04-13T09:39:33.610 回答
8

(根据 Jonathan 的评论更新)这里有两个视图,C++11 和 C++14 视图。早在 2013 年,C++11 的std::next定义不正确。它应该适用于迭代器,但由于看起来像是疏忽,当您将其传递给非迭代器时,它会导致严重失败。我认为其意图是 SFINAE 应该阻止这种情况;std::iterator_traits<X>应该会导致替换失败。

在 C++14 中,这个问题得到了解决。的定义std::next没有改变,但它的第二个参数 ( std::iterator_traits<>) 现在对于非迭代器来说是正确的空。这消除std::next了非迭代器的重载集。

相关声明(取自VS2013)是

template<class _FwdIt> inline
 _FwdIt next(_FwdIt _First,
 typename iterator_traits<_FwdIt>::difference_type _Off = 1)

如果可以为给定的参数实例化此函数,则应将其添加到重载集。

该函数是通过 Argument Dependent Lookup 和 Microsoft 的标头结构找到的。他们把std::next其中<xutility>共享的<string><iterator>

注意:_FwdItand_Off是实现命名空间的一部分。不要自己使用前导下划线。

于 2015-04-13T09:24:48.180 回答
5

实际上std::next()是一个定义的函数,<iterator>它返回传递给的下一个迭代器std::next()。您的代码使用 gcc-4.9.2 在我的计算机上运行。更多:http ://en.cppreference.com/w/cpp/iterator/next

我使用的代码:

#include<string>
#include <iostream>
int next(std::string param){
    std::cout<<param<<std::endl;
    return 0;
}

void foo(){
    next(std::string{ "abc" });
}

int main()
{
    foo();
    return 0;
}

同样在 ideone 上:http: //ideone.com/QVxbO4

于 2015-04-12T21:51:11.500 回答
4

由于调用中使用的参数 - std::string - 是在命名空间中声明的,因此编译器std::next通过所谓的参数依赖查找找到了标准函数std

于 2015-04-12T21:49:45.770 回答