为什么以相同方式创建的包含字符串的两个元组不相等?
Why are my two tuples containing strings, created the same way, not equal?
我正在使用 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
class 是指针和大小的薄包装,并定义了检查值相等性的比较运算符。
问题与 C++20 无关,而是来自字符串文字的实现方式。答案例如在这里:
简而言之,您的程序属于“未定义 未指定行为”类别,因为它假定相同的 C-style 字符串文字具有相同的地址。这是因为像 "a" == "a"
这样的表达式比较的是地址,而不是内容。如果您使用 std::string
文字,例如 "one"s
、"one"sv
等,您的代码可以变得安全且可预测,请参阅 https://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s
auto
并不总是你的朋友。我认为在没有样板的情况下可靠地获得“正确”行为的正确方法是明确使用您知道具有 value-equality 的类型。然后你也可以省略 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", "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
可能 更可取,但是在 real-world 应用程序中,字符串可能需要在某处动态分配。
我正在使用 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
class 是指针和大小的薄包装,并定义了检查值相等性的比较运算符。
问题与 C++20 无关,而是来自字符串文字的实现方式。答案例如在这里:
简而言之,您的程序属于“未定义 未指定行为”类别,因为它假定相同的 C-style 字符串文字具有相同的地址。这是因为像 "a" == "a"
这样的表达式比较的是地址,而不是内容。如果您使用 std::string
文字,例如 "one"s
、"one"sv
等,您的代码可以变得安全且可预测,请参阅 https://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s
auto
并不总是你的朋友。我认为在没有样板的情况下可靠地获得“正确”行为的正确方法是明确使用您知道具有 value-equality 的类型。然后你也可以省略 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", "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
可能 更可取,但是在 real-world 应用程序中,字符串可能需要在某处动态分配。