【发布时间】:2011-10-18 15:56:13
【问题描述】:
问题描述:
考虑一些具有std::string name 成员的结构。为清楚起见,我们假设它是struct Human,代表有关人员的信息。除了name,它还可以有许多其他数据成员。
假设有一个容器std::vector<Human> vec,其中的对象已经按name 排序。另外为了清楚起见,假设所有名称都是唯一的。
问题是:有一些字符串 nameToFind 找出数组中是否存在具有该名称的元素。
解决方案和我的进展:
显而易见且自然的解决方案似乎是使用std::binary_search 函数执行二进制搜索。但是有一个问题:被搜索元素的类型(std::string)与容器中元素的类型(Human)不同,std::binary_search 需要一个规则来比较这些元素。我试图通过三种方式解决这个问题,如下所述。提供前两个只是为了说明我的解决方案的演变和我遇到的问题。我的主要问题是关于第三个问题。
尝试 1:将 std::string 转换为 Human。
写一个比较函数:
bool compareHumansByNames( const Human& lhs, const Human& rhs )
{
return lhs.name < rhs.name;
}
然后添加一个构造函数,它从std::string 构造一个Human 对象:
struct Human
{
Human( const std::string& s );
//... other methods
std::string name;
//... other members
};
并以下列形式使用 binary_search:
std::binary_search( vec.begin(), vec.end(), nameToFind, compareHumansByNames );
似乎有效,但出现了两个大问题:
首先,如何初始化除Human::name 之外的其他数据成员,尤其是在它们没有默认构造函数的情况下?设置魔法值可能会导致创建语义非法的对象。
其次,我们必须将此构造函数声明为非explicit 以允许在算法期间进行隐式转换。这样做的不良后果是众所周知的。
此外,每次迭代都会构造这样一个临时的Human 对象,结果可能会非常昂贵。
尝试 2:将 Human 转换为 std::string。
我们可以尝试将operator string () 添加到返回name 的Human 类中,然后使用两个std::strings 的比较。但是,由于以下原因,这种方法也很不方便:
首先,由于here 讨论的问题,代码不会立即编译。我们将不得不做更多工作以使编译器使用适当的operator <。
其次,“将人类转换为字符串”是什么意思?这种转换的存在会导致Human 类在语义上的错误使用,这是不可取的。
尝试 3:比较没有转化。
到目前为止我得到的最佳解决方案是创建一个
struct Comparator
{
bool operator() ( const Human& lhs, const std::string& rhs )
{
return lhs.name < rhs;
}
bool operator() ( const std::string& lhs, const Human& rhs )
{
return lhs < rhs.name;
}
};
并使用二分查找
binary_search( vec.begin(), vec.end(), nameToFind, Comparator() );
这编译和执行正确,一切似乎都正常,但有趣的部分从这里开始:
看看http://www.sgi.com/tech/stl/binary_search.html。这里说的是“ForwardIterator的值类型是与T相同的类型。”。相当混乱的限制,我的最后一个解决方案打破了它。让我们看看 C++ 标准对此有何评论:
25.3.3.4 binary_search
template<class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last,
const T& value);
template<class ForwardIterator, class T, class Compare>
bool binary_search(ForwardIterator first, ForwardIterator last,
const T& value, Compare comp);
要求:类型 T 是 LessThanComparable (20.1.2)。
没有明确说明ForwardIterator 的类型。但是,在20.1.2 中给出的LessThanComparable 的定义中,提到了相同类型 的两个元素的比较。这是我不明白的。是否确实意味着正在搜索的对象的类型和容器对象的类型必须相同,而我的解决方案打破了这一点限制 ?或者它不涉及使用comp比较器的情况,而仅涉及使用默认operator <进行比较的情况?在第一种情况下,我对如何使用std::binary_search 来解决这个问题而不遇到上述问题感到困惑。
提前感谢您的帮助并抽出时间阅读我的问题。
注意:我知道手动编写二分搜索不需要时间,并且会立即解决问题,但为了避免重新发明轮子,我想使用 std::binary_search。根据标准了解这种限制的存在对我来说也很有趣。
【问题讨论】:
-
我没有 C++98 或 03 标准的副本,但我的 C++0x 标准版本没有说明它们需要是 LessThanComparable。只有
operator<可以在两边进行比较(或者在比较函子版本的情况下,operator())。所以 C++0x 似乎已经消除了歧义,您的binary_search实现领先于曲线。 -
有趣的问题。出于好奇(为了后代),当您说“这可以正确编译和执行,一切正常。”时,您用什么实现进行了测试?
-
我认为 C++0x FDIS 并没有说任何限制这种类型的东西,只谈到了
e < value或comp(e, value)的订购要求,其中e在范围和value是发现者。 (我想这就是尼科尔已经说过的。) -
@André Caron:在 g++ 和 MSVS 上。实际上,我怀疑是否存在一个不起作用的实现,但我想了解标准在这个问题中的立场。
-
@Grigor:我也怀疑你会找到一个它不起作用的实现,但我仍然认为当某些东西声称可以工作时,说哪个实现是相关的。跨度>
标签: c++ algorithm search stl standards