问题描述
我正在使用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);
,您将得到期望的结果。 (在以前的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,"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
可能更可取,但是在实际应用中,无论如何都需要在任何地方动态分配字符串。