C ++,在实现函数`int next(std :: string param)`时出现奇怪的编译器错误
C++, curious compiler error when implementing a function `int next(std::string param)`
我被下面的代码咬得很厉害,浪费了很多宝贵的时间。
#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()
更改为其他名称,一切都很好。对我来说,这表明存在名称冲突,特别是名称 next
。我觉得这很奇怪,因为我没有使用像 using namespace std
这样的东西。据我所知,next
不是内置的 C++ 关键字(是吗?)。我查了 next
here,但它是 std::next
,正如我所说,我没有 using namespace std
。那么这场冲突是如何发生的呢?我如何防止将来发生类似的事情?还有哪些其他名称可能会导致这样的冲突?
由于所谓的参数相关查找,编译器找到了标准函数 std::next
,因为调用中使用的参数 - std::string - 在命名空间 std
中声明。
实际上 std::next()
是 <iterator>
中定义的函数,returns 下一个迭代器传递给 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
(根据乔纳森的评论更新)
这里有两个视图,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>
共享
注意:_FwdIt
和 _Off
是实现命名空间的一部分。不要自己使用前导下划线。
这里发生了几件事,以微妙的方式相互作用。
首先,使用 std::string
类型的参数对 next
的非限定调用意味着除了您自己的 next
函数外,标准函数模板 std::next
是由 Argument-dependent lookup (ADL) 找到。
名称查找找到您的 ::next
和标准库的 std::next
后,它会执行重载解析以查看哪个更适合您调用它时使用的参数。
std::next
的定义如下:
template <class ForwardIterator>
ForwardIterator next(ForwardIterator x,
typename std::iterator_traits<ForwardIterator>::difference_type n = 1);
这意味着当编译器执行重载决策时,它会将类型 std::string
替换为 std::iterator_traits<std::string>
.
在 C++14 iterator_traits
is not SFINAE-friendly which means that it is invalid to instantiate it with a type that is not an iterator. std::string
is not an iterator, so it's invalid. The SFINAE rule does not apply here, because the error is not in the immediate context 之前,因此对任何非迭代器 T
使用 iterator_traits<T>::difference_type
将产生硬错误,而不是替换失败。
您的代码应该可以在 C++14 中正常工作,或者使用已经提供 SFINAE 友好 iterator_traits
的不同标准库实现,例如 GCC 的库。我相信微软也会为 Visual Studio.
的下一个主要版本提供对 SFINAE 友好的 iterator_traits
现在为了让您的代码正常工作,您可以限定对 next
的调用,这样就不会执行 ADL:
::next(std::string{ "abc" });
这表示调用全局命名空间中的 next
,而不是可能通过非限定名称查找找到的任何其他 next
。
我被下面的代码咬得很厉害,浪费了很多宝贵的时间。
#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()
更改为其他名称,一切都很好。对我来说,这表明存在名称冲突,特别是名称 next
。我觉得这很奇怪,因为我没有使用像 using namespace std
这样的东西。据我所知,next
不是内置的 C++ 关键字(是吗?)。我查了 next
here,但它是 std::next
,正如我所说,我没有 using namespace std
。那么这场冲突是如何发生的呢?我如何防止将来发生类似的事情?还有哪些其他名称可能会导致这样的冲突?
由于所谓的参数相关查找,编译器找到了标准函数 std::next
,因为调用中使用的参数 - std::string - 在命名空间 std
中声明。
实际上 std::next()
是 <iterator>
中定义的函数,returns 下一个迭代器传递给 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
(根据乔纳森的评论更新)
这里有两个视图,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>
注意:_FwdIt
和 _Off
是实现命名空间的一部分。不要自己使用前导下划线。
这里发生了几件事,以微妙的方式相互作用。
首先,使用 std::string
类型的参数对 next
的非限定调用意味着除了您自己的 next
函数外,标准函数模板 std::next
是由 Argument-dependent lookup (ADL) 找到。
名称查找找到您的 ::next
和标准库的 std::next
后,它会执行重载解析以查看哪个更适合您调用它时使用的参数。
std::next
的定义如下:
template <class ForwardIterator>
ForwardIterator next(ForwardIterator x,
typename std::iterator_traits<ForwardIterator>::difference_type n = 1);
这意味着当编译器执行重载决策时,它会将类型 std::string
替换为 std::iterator_traits<std::string>
.
在 C++14 iterator_traits
is not SFINAE-friendly which means that it is invalid to instantiate it with a type that is not an iterator. std::string
is not an iterator, so it's invalid. The SFINAE rule does not apply here, because the error is not in the immediate context 之前,因此对任何非迭代器 T
使用 iterator_traits<T>::difference_type
将产生硬错误,而不是替换失败。
您的代码应该可以在 C++14 中正常工作,或者使用已经提供 SFINAE 友好 iterator_traits
的不同标准库实现,例如 GCC 的库。我相信微软也会为 Visual Studio.
iterator_traits
现在为了让您的代码正常工作,您可以限定对 next
的调用,这样就不会执行 ADL:
::next(std::string{ "abc" });
这表示调用全局命名空间中的 next
,而不是可能通过非限定名称查找找到的任何其他 next
。