我们多次被以下错误所困扰:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void print(int* pn) { cout << *pn << " "; }
int main() {
int* n1 = new int(1);
int* n2 = new int(2);
int* n3 = new int(3);
vector<int*> v;
v.push_back(n1);
v.push_back(n2);
v.push_back(n3);
sort(v.begin(), v.end()); // Here be dragons!
for_each(v.begin(), v.end(), print);
cout << endl;
delete n1; delete n2; delete n3;
}
问题是std :: sort比较整数指针而不是整数,这不是程序员想要的。更糟糕的是,输出可能看起来是正确的和确定的(考虑新的或在堆栈上分配的地址的顺序)。根本问题是sort最终调用operator&lt;对于T,当T是指针类型时,这很少是个好主意。
有没有办法阻止这种情况或者至少得到编译器警告?例如,当T是指针时,有没有办法创建一个自定义版本的std :: sort 需要一个比较函数?
答案 0 :(得分:12)
IMO,程序员应该知道std::sort
假定容器存储值。如果您需要不同的行为进行比较,那么您需要提供比较功能。例如。 (另):
template<typename T>
inline bool deref_compare(T* t1, T* t2) { return *t1 < *t2; }
//...
std::sort(v.begin(), v.end(), deref_compare<int>);
修改强>
FWIW,Jacob's answer最接近直接完成你想要的东西。可能有一些方法可以进一步概括它。
答案 1 :(得分:2)
我对一般的指针没有一个好的答案,但如果你使用任何类型的智能指针,你可以限制比较 - 例如boost :: shared_ptr。
#include <boost/shared_ptr.hpp>
using namespace std;
template<class T>
bool operator<(boost::shared_ptr<T> a, boost::shared_ptr<T> b)
{
return boost::shared_ptr<T>::dont_compare_pointers;
}
int main () {
boost::shared_ptr<int> A;
boost::shared_ptr<int> B;
bool i = A < B;
}
输出:
In function 'bool operator<(boost::shared_ptr<T>, boost::shared_ptr<T>) [with T = int]':
t.cpp:15: instantiated from here
Line 8: error: 'dont_compare_pointers' is not a member of 'boost::shared_ptr<int>'
compilation terminated due to -Wfatal-errors.
因此,您可以使用智能指针,或创建自己的智能指针包装器。这对于你想要的东西来说非常重要,所以如果你创建了一个包装来检测这种情况,我建议你只在调试模式下使用它。所以创建一个宏(呃,我知道)并用它来声明指针。
#ifdef DEBUG
#define pointer(x) pointer_wrapper<X>
#else
#define pointer(x) x*
#endif
当然,这还需要你的程序员使用它!
答案 2 :(得分:2)
对于一般的指针,你可以这样做:
#include <ctime>
#include <vector>
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <type_traits>
namespace util {
struct sort_pointers {
bool operator() ( int *a, int *b ) {
return *a < *b;
}
};
template <typename T, bool is_pointer = !std::tr1::is_pointer<T>::value>
struct sort_helper {
typedef std::less<T> wont_compare_pointers;
};
template <typename T>
struct sort_helper<T,false> {
};
template <typename Iterator>
void sort( Iterator start, Iterator end )
{
std::sort( start,
end,
sort_helper
<
typename Iterator::value_type
>::wont_compare_pointers() );
}
template <typename Iterator, class Func>
void sort( Iterator start, Iterator end, Func f ) {
std::sort( start, end, f );
}
}
int main() {
std::vector<int> v1;
std::vector<int*> v2;
srand(time(0));
for( int i = 0; i < 10; ++i ) {
v1.push_back(rand());
}
util::sort( v1.begin(), v1.end() );
for( int i = 0; i < 10; ++i ) {
v2.push_back(&v1[i]);
}
/* util::sort( v2.begin(), v2.end() ); */ //fails.
util::sort( v2.begin(), v2.end(), util::sort_pointers() );
return 0;
}
std::tr1::is_pointer
就是它在Visual Studio 2008中调用的内容,但我认为Boost也有一个,而较新的编译可能会将其作为std::is_pointer
提供。我相信有人能够编写更漂亮的解决方案,但这似乎有效。
但我必须说,我同意cogwheel,没有理由这样做,程序员应该能够看出这是否会成为问题并采取相应的行动。
增加:
你可以更多地概括一下,自动选择一个将取消引用指针并比较值的仿函数:
namespace util {
template <typename T>
struct sort_pointers {
bool operator() ( T a, T b ) {
return *a < *b;
}
};
template <typename T, bool is_pointer = !std::tr1::is_pointer<T>::value>
struct sort_helper {
typedef std::less<T> compare;
};
template <typename T>
struct sort_helper<T,false> {
typedef sort_pointers<T> compare;
};
template <typename Iterator>
void sort( Iterator start, Iterator end )
{
std::sort( start,
end,
sort_helper
<
typename Iterator::value_type
>::compare() );
}
}
这样你就不必考虑是否提供了比较与否的指针,它会自动整理出来。