vector/algorithm 和 iostream 之间不兼容
Incompatibility between vector/algorithm and iostream
旧版本的 Borland C++ 编译器在包含 <iostream>
时使用 std::remove 的正确重载似乎有问题。
要重现此错误,您需要旧版本的 Borland C++ Builder(例如 C++ Builder 6)和这个非常小的代码片段:
#include <vector>
#include <algorithm>
void __fastcall TFormMain::Button1Click(TObject *Sender)
{
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 );
}
(我知道:这段代码什么都不做,但至少它编译...)
一切正常,直到您在代码中的某处包含 iostream:
#include <vector>
#include <iostream>
#include <algorithm>
void __fastcall TFormMain::Button1Click(TObject *Sender)
{
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 );
}
这会导致一些编译错误:
[C++ Fehler] UnitFormMain.cpp(22): E2034 Konvertierung von 'int *' nach 'const char *' nicht möglich
[C++ Fehler] UnitFormMain.cpp(22): E2342 Keine Übereinstimmung des Typs beim Parameter '__path' ('const char *' erwartet, 'int *' erhalten)
[C++ Fehler] UnitFormMain.cpp(22): E2227 Zu viele Parameter im Aufruf von std::remove(const char *)
英文:
[C++ Error] UnitFormMain.cpp(22): E2034 Cannot convert 'int *' to 'const char *'
[C++ Error] UnitFormMain.cpp(22): E2342 Type mismatch in parameter '__path' (wanted 'const char *', got 'int *')
[C++ Error] UnitFormMain.cpp(22): E2227 Extra parameter in call to std::remove(const char *)
我的问题是:
有没有什么 elegant/correct 方法可以在不将代码分离到不同文件的情况下解决这个问题?
您可以尝试一些方法,但请对此持保留态度,因为我没有可用的 C++ 构建器来测试它。
using 声明可以将名称从一个名称空间引入另一个名称空间,可用于限定查找。但是,只会引入在 using 声明处可见的重载。因此,如果我们只获取我们 想要 的重载,并在新命名空间上进行限定名称查找,就不会有歧义。这是代码:
#include <vector>
#include <algorithm>
namespace resolve_std {
using std::remove;
}
#include <iostream>
void __fastcall TFormMain::Button1Click(TObject *Sender)
{
std::vector< int > Selection;
// Should only use the overloads introduced prior to including <iostream>
resolve_std::remove( Selection.begin(), Selection.end(), 10 );
}
BCB6 有两个 STL 库 - STLPort 和 RogueWave。 STLPort 是默认设置,提供 RogueWave 是为了向后兼容以前的 BCB 版本。
您的代码试图从 STLPort 的 <algorithm>
header 调用 STL std::remove()
函数(它实际上在 <stl/_algo.h>
中定义):
template <class _ForwardIter, class _Tp>
_STLP_INLINE_LOOP _ForwardIter
remove(_ForwardIter __first, _ForwardIter __last, const _Tp& __value)
但是,C 运行时库有一个 single-parameter remove()
函数,在 <stdio.h>
header:
int _RTLENTRY _EXPFUNC remove(const char * __path);
此 C 函数由 <cstdio>
header 引入 C++ 中的 std
命名空间,其中包含 STLPort 的 <algorithm>
header。在 <algorithm>
在 <stl/_algo.h>
之前包含 <cstdio>
的地方甚至还有关于 remove()
的评论:
# if ! defined (_STLP_USE_NAMESPACES)
// remove() conflicts, <cstdio> should always go first
# include <cstdio>
# endif
# ifndef _STLP_INTERNAL_ALGO_H
# include <stl/_algo.h>
# endif
甚至 _algo.h
也有类似的评论:
# ifdef __SUNPRO_CC
// remove() conflict
# include <cstdio>
# endif
所以STLPort总是在定义自己的remove()
算法之前包含<cstdio>
,应该来处理命名冲突。
但是,话虽如此,您看到的所有错误都是由于编译器认为您正在尝试调用 1 参数 std::remove()
C 函数而不是 3 参数 std::remove()
STL函数。为什么编译器这么认为,我不知道。这可能是 BCB6 如何解决重载的编译器错误。
然而,这个问题只影响 STLPort,不影响 RogueWave,因为 RogeWave 的 <algorithm>
header 不会导致 <cstdio>
被包含(事实上,RogueWave 甚至没有尝试解决 C 和 STL 之间与 remove()
的任何命名冲突,就像 STLPort 所做的那样)。
因此,一种解决方案是启用 RogueWave 而不是 STLPort,方法是在任何 STL headers:
之前定义 _USE_OLD_RW_STL
#define _USE_OLD_RW_STL
// alternatively, add `_USE_OLD_RW_STL` to the Conditionals
// list in the Project Options...
#include <vector>
#include <iostream>
#include <algorithm>
...
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
否则,如果你想使用STLPort,你可以使用Kamil Cuk在评论中提到的建议:
#include <vector>
#define remove _mask_remove
#include <iostream>
#undef remove
#include <algorithm>
...
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
或者,使用 answer proposed by StoryTeller:
#include <vector>
#include <algorithm>
namespace resolve_std {
using std::remove;
}
#include <iostream>
...
std::vector< int > Selection;
resolve_std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
我已经在 BCB6 中测试了所有这些解决方案,它们都适用于这种情况。
旧版本的 Borland C++ 编译器在包含 <iostream>
时使用 std::remove 的正确重载似乎有问题。
要重现此错误,您需要旧版本的 Borland C++ Builder(例如 C++ Builder 6)和这个非常小的代码片段:
#include <vector>
#include <algorithm>
void __fastcall TFormMain::Button1Click(TObject *Sender)
{
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 );
}
(我知道:这段代码什么都不做,但至少它编译...)
一切正常,直到您在代码中的某处包含 iostream:
#include <vector>
#include <iostream>
#include <algorithm>
void __fastcall TFormMain::Button1Click(TObject *Sender)
{
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 );
}
这会导致一些编译错误:
[C++ Fehler] UnitFormMain.cpp(22): E2034 Konvertierung von 'int *' nach 'const char *' nicht möglich
[C++ Fehler] UnitFormMain.cpp(22): E2342 Keine Übereinstimmung des Typs beim Parameter '__path' ('const char *' erwartet, 'int *' erhalten)
[C++ Fehler] UnitFormMain.cpp(22): E2227 Zu viele Parameter im Aufruf von std::remove(const char *)
英文:
[C++ Error] UnitFormMain.cpp(22): E2034 Cannot convert 'int *' to 'const char *'
[C++ Error] UnitFormMain.cpp(22): E2342 Type mismatch in parameter '__path' (wanted 'const char *', got 'int *')
[C++ Error] UnitFormMain.cpp(22): E2227 Extra parameter in call to std::remove(const char *)
我的问题是:
有没有什么 elegant/correct 方法可以在不将代码分离到不同文件的情况下解决这个问题?
您可以尝试一些方法,但请对此持保留态度,因为我没有可用的 C++ 构建器来测试它。
using 声明可以将名称从一个名称空间引入另一个名称空间,可用于限定查找。但是,只会引入在 using 声明处可见的重载。因此,如果我们只获取我们 想要 的重载,并在新命名空间上进行限定名称查找,就不会有歧义。这是代码:
#include <vector>
#include <algorithm>
namespace resolve_std {
using std::remove;
}
#include <iostream>
void __fastcall TFormMain::Button1Click(TObject *Sender)
{
std::vector< int > Selection;
// Should only use the overloads introduced prior to including <iostream>
resolve_std::remove( Selection.begin(), Selection.end(), 10 );
}
BCB6 有两个 STL 库 - STLPort 和 RogueWave。 STLPort 是默认设置,提供 RogueWave 是为了向后兼容以前的 BCB 版本。
您的代码试图从 STLPort 的 <algorithm>
header 调用 STL std::remove()
函数(它实际上在 <stl/_algo.h>
中定义):
template <class _ForwardIter, class _Tp>
_STLP_INLINE_LOOP _ForwardIter
remove(_ForwardIter __first, _ForwardIter __last, const _Tp& __value)
但是,C 运行时库有一个 single-parameter remove()
函数,在 <stdio.h>
header:
int _RTLENTRY _EXPFUNC remove(const char * __path);
此 C 函数由 <cstdio>
header 引入 C++ 中的 std
命名空间,其中包含 STLPort 的 <algorithm>
header。在 <algorithm>
在 <stl/_algo.h>
之前包含 <cstdio>
的地方甚至还有关于 remove()
的评论:
# if ! defined (_STLP_USE_NAMESPACES)
// remove() conflicts, <cstdio> should always go first
# include <cstdio>
# endif
# ifndef _STLP_INTERNAL_ALGO_H
# include <stl/_algo.h>
# endif
甚至 _algo.h
也有类似的评论:
# ifdef __SUNPRO_CC
// remove() conflict
# include <cstdio>
# endif
所以STLPort总是在定义自己的remove()
算法之前包含<cstdio>
,应该来处理命名冲突。
但是,话虽如此,您看到的所有错误都是由于编译器认为您正在尝试调用 1 参数 std::remove()
C 函数而不是 3 参数 std::remove()
STL函数。为什么编译器这么认为,我不知道。这可能是 BCB6 如何解决重载的编译器错误。
然而,这个问题只影响 STLPort,不影响 RogueWave,因为 RogeWave 的 <algorithm>
header 不会导致 <cstdio>
被包含(事实上,RogueWave 甚至没有尝试解决 C 和 STL 之间与 remove()
的任何命名冲突,就像 STLPort 所做的那样)。
因此,一种解决方案是启用 RogueWave 而不是 STLPort,方法是在任何 STL headers:
之前定义_USE_OLD_RW_STL
#define _USE_OLD_RW_STL
// alternatively, add `_USE_OLD_RW_STL` to the Conditionals
// list in the Project Options...
#include <vector>
#include <iostream>
#include <algorithm>
...
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
否则,如果你想使用STLPort,你可以使用Kamil Cuk在评论中提到的建议:
#include <vector>
#define remove _mask_remove
#include <iostream>
#undef remove
#include <algorithm>
...
std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
或者,使用 answer proposed by StoryTeller:
#include <vector>
#include <algorithm>
namespace resolve_std {
using std::remove;
}
#include <iostream>
...
std::vector< int > Selection;
resolve_std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
我已经在 BCB6 中测试了所有这些解决方案,它们都适用于这种情况。