通过三元运算符返回 std::string - returns 垃圾
Returning std::string via ternary operator - returns garbage
我有以下代码:
#include <string>
#include <cstdio>
std::string name = "Ternary Return Test";
std::string *pname = &name;
const std::string &getName ()
{
return pname ? *pname : "(unnamed)";
}
int main (int argc, char *argv[])
{
const std::string &str = getName();
printf ("Name is \"%s\"\n",str.c_str());
printf ("pName is \"%s\"\n",pname->c_str());
return 0;
}
函数 getName() 取消引用 pname 指针,return它的值作为引用。
据我了解,return 通过取消引用指针来引用某物是完全有效的。
为了避免 NULL 指针取消引用,函数 returns 字符串 "unnamed",在(在这种情况下不可能)情况下,当 pname 指针为 NULL 时。
但是,三元运算符破坏了函数。代码编译,但打印如下:
Name is "�
@"
pName is "Ternary Return Test"
通过函数获取的值已损坏。我不明白为什么。
在其他情况下,类似的代码会产生分段错误。那么,这里发生了未定义的行为?
是的,在 pname 为 NULL 的情况下,它将 return 对未定义的临时引用。但事实并非如此 - 我正在 return 引用一个非临时值,它应该是完全有效的。
正如我所读,三元运算符将第三个表达式“(unnamed)”转换为第二个表达式的类型 (std::string)。但就我而言,甚至没有使用第三个表达式。
But this is not the case - I am returning a reference to a non-temporary value, which should be perfectly valid.
你会的,但是你不再有非临时值了。 [expr.cond]/4.3 有
If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has (possibly cv-qualified) class type:
- if T1 and T2 are the same class type (ignoring cv-qualification) and T2 is at least as cv-qualified as T1, the target type is T2,
归根结底,因为第三个操作数是纯右值,所以整个表达式都是纯右值,这意味着您总是有未定义的行为,因为您总是返回对临时值的引用。
的三元运算符类型
pname ? *pname : "(unnamed)"
是 std::string
(我们有 std::string&
和 const char*
)
所以相当于pname ? std::string{*pname} : std::string{"(unnamed)"}
.
所以你 return 从临时变量引用(在这两种情况下)(所以悬空引用,在你使用它时导致 UB)。
如果您使用 if
而不是三进制,您将得到预期的错误
const std::string& getName()
{
if pname { return *pname; } // OK here
return "(unnamed)"; // Dangling pointer here
}
一个解决方案可能是;
const std::string &getName ()
{
static const std::string unnamed = "(unnamed)";
return pname ? *pname : unnamed;
}
所以两边都是左值,common类型现在是const std::string &
(std::string &
和const std::string&
)。
正如 Yksisarvinen 指出的那样,这个问题是一生的问题。 "(unnamed)"
有静态存储持续时间,但那不是你 return。你 return std::string{"(unnamed)"}
,准确地说是参考。那个未命名的临时文件甚至在函数 returns.
之前就消失了
修复:
std::string const& getName ()
{
static const std::string defaultValue = "(unnamed)";
return pname ? *pname : defaultValue;
}
仅供参考(悬挂参考 UB 已得到很好的描述),这可能类似于
const std::string &getName ()
{
static const std::string unnamed("unnamed");
return pname ? *pname : unnamed;
}
我有以下代码:
#include <string>
#include <cstdio>
std::string name = "Ternary Return Test";
std::string *pname = &name;
const std::string &getName ()
{
return pname ? *pname : "(unnamed)";
}
int main (int argc, char *argv[])
{
const std::string &str = getName();
printf ("Name is \"%s\"\n",str.c_str());
printf ("pName is \"%s\"\n",pname->c_str());
return 0;
}
函数 getName() 取消引用 pname 指针,return它的值作为引用。 据我了解,return 通过取消引用指针来引用某物是完全有效的。 为了避免 NULL 指针取消引用,函数 returns 字符串 "unnamed",在(在这种情况下不可能)情况下,当 pname 指针为 NULL 时。
但是,三元运算符破坏了函数。代码编译,但打印如下:
Name is "�
@"
pName is "Ternary Return Test"
通过函数获取的值已损坏。我不明白为什么。 在其他情况下,类似的代码会产生分段错误。那么,这里发生了未定义的行为?
是的,在 pname 为 NULL 的情况下,它将 return 对未定义的临时引用。但事实并非如此 - 我正在 return 引用一个非临时值,它应该是完全有效的。
正如我所读,三元运算符将第三个表达式“(unnamed)”转换为第二个表达式的类型 (std::string)。但就我而言,甚至没有使用第三个表达式。
But this is not the case - I am returning a reference to a non-temporary value, which should be perfectly valid.
你会的,但是你不再有非临时值了。 [expr.cond]/4.3 有
If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has (possibly cv-qualified) class type:
- if T1 and T2 are the same class type (ignoring cv-qualification) and T2 is at least as cv-qualified as T1, the target type is T2,
归根结底,因为第三个操作数是纯右值,所以整个表达式都是纯右值,这意味着您总是有未定义的行为,因为您总是返回对临时值的引用。
pname ? *pname : "(unnamed)"
是 std::string
(我们有 std::string&
和 const char*
)
所以相当于pname ? std::string{*pname} : std::string{"(unnamed)"}
.
所以你 return 从临时变量引用(在这两种情况下)(所以悬空引用,在你使用它时导致 UB)。
如果您使用 if
而不是三进制,您将得到预期的错误
const std::string& getName()
{
if pname { return *pname; } // OK here
return "(unnamed)"; // Dangling pointer here
}
一个解决方案可能是;
const std::string &getName ()
{
static const std::string unnamed = "(unnamed)";
return pname ? *pname : unnamed;
}
所以两边都是左值,common类型现在是const std::string &
(std::string &
和const std::string&
)。
正如 Yksisarvinen 指出的那样,这个问题是一生的问题。 "(unnamed)"
有静态存储持续时间,但那不是你 return。你 return std::string{"(unnamed)"}
,准确地说是参考。那个未命名的临时文件甚至在函数 returns.
修复:
std::string const& getName ()
{
static const std::string defaultValue = "(unnamed)";
return pname ? *pname : defaultValue;
}
仅供参考(悬挂参考 UB 已得到很好的描述),这可能类似于
const std::string &getName ()
{
static const std::string unnamed("unnamed");
return pname ? *pname : unnamed;
}