为什么我的两个元组包含以相同方式创建的字符串,而不相等?

问题描述

我正在使用Microsoft Visual C ++作为C ++ 20程序编译以下程序:

#include <iostream>
#include <tuple>

int main()
{
    auto t1 = std::make_tuple("one","two","three");
    auto t2 = std::make_tuple("one","three");
    
    std::cout << "(t1 == t2) is " << std::boolalpha << (t1 == t2) << "\n";
    std::cout << "(t1 != t2) is " << std::boolalpha << (t1 != t2) << "\n";

    return 0;
}

运行它时,我看到以下输出

(t1 == t2) is false
(t1 != t2) is true

元组相同,那么为什么比较结果错误?我该如何解决

解决方法

您正在比较指向字符缓冲区而不是字符串缓冲区的指针。

有时候,编译器会将两个不同的"one"放入同一个缓冲区,有时不会。

就您而言,不是。可能是调试版本。

添加#include <string_view>,然后

using namespace std::literals;

auto t1 = std::make_tuple("one"sv,"two"sv,"three"sv);
auto t2 = std::make_tuple("one"sv,"three"sv);

,您将得到期望的结果。 (在以前的编译器中,请使用<string>""s而不是<string_view>""sv。)

,

"one"是什么类型?这不是字符串,而是字符串文字。

您的问题基本上可以归结为以下代码:

char const* a = "one";
char const* b = "one";

std::cout << "(a == b) is " << std::boolalpha << (a == b) << "\n";
std::cout << "(a != b) is " << std::boolalpha << (a != b) << "\n";

最有可能输出相同结果的人。

这是因为字符串文字将衰减为char const*。比较两个指针会比较它们在内存中的位置。现在,这取决于编译器是否将字符串文字折叠为一个。如果字符串文字被折叠,则它们将相等,如果不相等,则它们将不相等。不同的优化级别会有所不同。

那您如何解决比较问题?

最好使用std::string_view,因为您似乎不需要拥有或更改其内容:

using namespace std::literals;

// ... 

auto t1 = std::make_tuple("one"sv,"three"sv);

std::string_view类是一个围绕指针和大小的瘦包装器,并定义了一个比较运算符,用于检查值的相等性。

,

该问题与C ++ 20无关,但是来自如何实现字符串文字。答案例如在这里:

Why do (only) some compilers use the same address for identical string literals?

简而言之,您的程序属于“ 未定义未指定行为”类别,因为它假定相同的C样式字符串文字具有相同的地址。这是因为"a" == "a"之类的表达式比较地址而不是内容。如果您使用std::string"one"s等字面量,请参见https://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s

,这样可以使代码安全且可预测。 ,

auto并不总是您的朋友。我认为,在没有样板的情况下可靠地获得“正确”行为的正确方法是显式使用您知道具有价值平等的类型。然后,您也可以省略make_tuple并仅使用Initialiser-list构造函数:

#include <string>
#include <tuple>
#include <iostream>

typedef std::tuple<std::string,std::string,std::string> StrTriple;

int main() {
  
  StrTriple t1{"one","two","three"};
  StrTriple t2{"one","three"};

  std::cout << "(t1 == t2) is " << std::boolalpha << (t1 == t2) << "\n";
  std::cout << "(t1 != t2) is " << std::boolalpha << (t1 != t2) << "\n";

    return 0;
}

毫无疑问,有人会争辩说std::string的内存管理会产生不必要的开销。 string_view 可能更可取,但是在实际应用中,无论如何都需要在任何地方动态分配字符串。