4

我想将一堆 const char 指针保存到 std::set 容器 [1] 中。std::set 模板需要一个比较器函子,标准 C++ 库提供了 std::less,但它的实现是基于直接比较两个键,这不是指针的标准。

我知道我可以定义自己的仿函数并通过将指针转换为整数并进行比较来实现 operator(),但是有没有更干净、“标准”的方法呢?

请不要建议创建 std::strings - 这是浪费时间和空间。字符串是静态的,因此可以根据它们的地址比较它们的(不)相等性。

1:指针指向静态字符串,所以它们的生命周期没有问题——它们不会消失。

4

8 回答 8

8

如果你不想将它们包装在std::strings 中,你可以定义一个仿函数类:

struct ConstCharStarComparator
{
  bool operator()(const char *s1, const char *s2) const
  {
    return strcmp(s1, s2) < 0;
  }
};

typedef std::set<const char *, ConstCharStarComparator> stringset_t;
stringset_t myStringSet;
于 2008-10-24T21:34:57.680 回答
3

“优化方式”

如果我们忽略“过早的优化是万恶之源”,标准的做法是添加一个比较器,这很容易写:

struct MyCharComparator
{
   bool operator()(const char * A, const char * B) const
   {
      return (strcmp(A, B) < 0) ;
   }
} ;

与 a 一起使用:

std::set<const char *, MyCharComparator>

标准方式

用一个:

std::set<std::string>

即使您将静态 const char * 放入其中,它也会起作用(因为 std::string 与 const char * 不同,其内容具有可比性)。

当然,如果你需要提取数据,你必须通过std::string.c_str()来提取数据。另一方面,但由于它是一个集合,我想您只想知道“AAA”是否在集合中,而不是提取“AAA”的值“AAA”。

注意:我确实读过“请不要建议创建 std::strings”,但是,你问的是“标准”方式......

“从不做”的方式

我在回答后注意到以下评论:

请不要建议创建 std::strings - 这是浪费时间和空间。字符串是静态的,因此可以根据它们的地址来比较它们的(不)相等性

这有点 C 的味道(使用已弃用的“静态”关键字,用于 std::string bashing 的可能过早优化,以及通过它们的地址进行字符串比较)。

无论如何,你不想通过它们的地址来比较你的字符串。因为我猜你想要的最后一件事是有一个包含:

{ "AAA", "AAA", "AAA" }

当然,如果只使用相同的全局变量来包含字符串,那就另当别论了。

在这种情况下,我建议:

std::set<const char *>

当然,如果您比较具有相同内容但变量/地址不同的字符串,它将不起作用。

而且,当然,如果静态 const char *字符串是在标头中定义的,则它不适用于这些字符串。

但这是另一个故事。

于 2008-10-24T21:32:42.223 回答
3

继续使用默认排序,即 less<>。该标准保证 less 即使对于指向不同对象的指针也有效:

“对于更大、更少、greater_equal 和 less_equal 模板,任何指针类型的特化都会产生一个总顺序,即使内置运算符 <、>、<=、>= 没有。”

保证完全适用于您的set<const char*>.

于 2008-10-25T12:57:22.243 回答
0

根据“一堆”的大小,我倾向于std::string在集合中存储相应的一堆 s。这样您就不必编写任何额外的胶水代码。

于 2008-10-24T21:30:50.387 回答
0

集合必须包含const char*吗?

立即想到的是将字符串存储在 a 中std::string,然后将它们放入std::set. 这将允许比较没有问题,并且您始终可以const char*通过简单的函数调用获得原始数据:

const char* data = theString.c_str();
于 2008-10-24T21:33:12.440 回答
0

要么使用比较器,要么使用要包含在集合中的包装器类型。(注意:std::string 也是一个包装器......)

const char* a("a");
const char* b("b");

struct CWrap {
    const char* p;
    bool operator<(const CWrap& other) const{
        return strcmp( p, other.p ) < 0;
    }
    CWrap( const char* p ): p(p){}
};

std::set<CWrap> myset;
myset.insert(a);
myset.insert(b);
于 2008-10-24T21:49:47.540 回答
0

其他人已经发布了很多解决方案,展示了如何与 进行词法比较const char*,所以我不会打扰。

请不要建议创建 std::strings - 这是浪费时间和空间。

如果std::string是浪费时间和空间,那么std::set也可能是浪费时间和空间。a 中的每个元素std::set都与空闲存储区分开分配。根据您的程序使用集合的方式,这可能会比std::set's O(log n) 查找对性能的帮助更大。std::vector根据集合的预期生命周期,使用其他数据结构(例如 sorted 或静态分配的在编译时排序的数组)可能会获得更好的结果。

标准 C++ 库提供 std::less,但它的实现是基于直接比较两个键,这不是指针的标准。

字符串是静态的,因此可以根据它们的地址比较它们的(不)相等性。

这取决于指针指向的内容。如果所有的键都是从同一个数组分配的,那么使用operator<来比较指针不是未定义的行为。

包含单独静态字符串的数组示例:

static const char keys[] = "apple\0banana\0cantaloupe";

如果您创建一个std::set<const char*>并用指向该数组的指针填充它,它们的顺序将是明确定义的。

但是,如果字符串都是单独的字符串文字,则比较它们的地址很可能会涉及未定义的行为。它是否有效取决于您的编译器/链接器实现、您如何使用它以及您的期望。

如果您的编译器/链接器支持字符串池并启用了它,那么重复的字符串文字应该具有相同的地址,但在所有情况下都可以保证吗?依靠链接器优化来获得正确的功能是否安全?

如果您仅在一个翻译单元中使用字符串文字,则集合排序可能基于字符串首次使用的顺序,但如果您将另一个翻译单元更改为使用相同的字符串文字之一,则集合排序可能会更改。

我知道我可以定义自己的仿函数并通过将指针转换为整数并比较它们来实现 operator()

将指针投射到uintptr_t使用指针比较似乎没有任何好处。无论哪种方式,结果都是相同的:特定于实现。

于 2008-10-25T07:33:55.490 回答
-1

由于性能原因,您可能不想使用 std::string 。

我正在运行 MSVC 和 gcc,他们似乎都不介意这一点:

bool foo = "blah" < "grar";

编辑:但是,在这种情况下的行为是未指定的。看评论...

他们也不抱怨std::set<const char*>

如果您使用的编译器确实会抱怨,我可能会继续使用您建议的将指针转换为ints 的仿函数。

编辑:嘿,我被否决了......尽管是这里为数不多的最直接回答他的问题的人之一。我是 Stack Overflow 的新手,如果发生这种情况,有什么方法可以保护自己吗?话虽如此,我会在这里尝试:

问题不是在寻找std::string解决方案。每次你std::string在集合中输入一个 in 时,它都需要复制整个字符串(直到 C++0x 是标准的,无论如何)。此外,每次进行集合查找时,都需要进行多个字符串比较。

但是,将指针存储在集合中不会导致字符串复制(您只是在复制指针),并且每次比较都是对地址的简单整数比较,而不是字符串比较。

该问题表明存储指向字符串的指针很好,我认为没有理由我们都应该立即假设该语句是错误的。如果您知道自己在做什么,那么使用const char*over eachstd::string或调用strcmp. 是的,它不太安全,更容易出错,但这些是性能的常见权衡,并且由于问题从未说明应用程序,我认为我们应该假设他已经考虑了利弊并决定支持性能.

于 2008-10-24T21:42:45.090 回答