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



我正在使用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", "two", "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, "two"sv, "three"sv);

你会得到你所期望的。(在c++17之前的编译器中,使用<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, "two"sv, "three"sv);
auto t2 = std::make_tuple("one"sv, "two"sv, "three"sv);

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

这个问题与C++20无关,但来自于字符串文本的实现方式。答案是这里的例子:

为什么(只有(一些编译器对相同的字符串文字使用相同的地址?

简而言之,您的程序属于">未定义的未指定的行为";,因为它假定相同的C样式字符串文字具有相同的地址。这是因为像"a" == "a"这样的表达式比较的是地址,而不是内容。如果您使用std::string文字,如"one"s"one"sv等,您的代码可以变得安全和可预测,请参阅https://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s

auto并不总是你的朋友。我认为,在没有样板的情况下可靠地获得"正确"行为的正确方法是显式使用一个你知道具有值相等的类型。然后,您也可以省略make_tuple,只需使用initializer列表构造函数:

#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", "two", "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可能是首选,但在现实世界的应用程序中,字符串无论如何都需要动态分配到某个地方。

最新更新