nev*_*stn 23 c++ gcc ternary-operator c++11 clang++
这段代码中有一些非常明显的事情:
float a = 1.;
const float & x = true ? a : 2.; // Note: `2.` is a double
a = 4.;
std::cout << a << ", " << x;
Run Code Online (Sandbox Code Playgroud)
clang和gcc输出:
4, 1
Run Code Online (Sandbox Code Playgroud)
人们会天真地期望两次打印相同的值,但事实并非如此.这里的问题与参考无关.有一些有趣的规则决定了它的类型? :
.如果两个参数的类型不同并且可以进行转换,则它们将使用临时的.该引用将指向临时的? :
.
上面的示例编译得很好,在编译时可能会也可能不会发出警告,-Wall
具体取决于编译器的版本.
这是一个例子,说明在看似合法的代码中出错这么容易:
template<class Iterator, class T>
const T & min(const Iterator & iter, const T & b)
{
return *iter < b ? *iter : b;
}
int main()
{
// Try to remove the const or convert to vector of floats
const std::vector<double> a(1, 3.0);
const double & result = min(a.begin(), 4.);
cout << &a[0] << ", " << &result;
}
Run Code Online (Sandbox Code Playgroud)
如果您的逻辑在此代码之后假定a[0]
将反映任何更改result
,则在?:
创建临时的情况下将是错误的.此外,如果在某些时候你制作了一个指针result
并且你在result
超出范围之后使用它,那么尽管你的原始版本a
没有超出范围,但会出现分段错误.
我觉得有充分的理由不使用此形式超出此处提到的"可维护性和阅读问题" ,尤其是在编写模板化代码时,您的某些类型和它们的常量可能无法控制.
所以我的问题是,const &
在三元运算符上使用s 是否安全?
PS Bonus示例1,额外的并发症(另见此处):
float a = 0;
const float b = 0;
const float & x = true ? a : b;
a = 4;
cout << a << ", " << x;
Run Code Online (Sandbox Code Playgroud)
铿锵输出:
4, 4
Run Code Online (Sandbox Code Playgroud)
gcc 4.9.3输出:
4, 0
Run Code Online (Sandbox Code Playgroud)
使用clang这个例子编译并按预期运行,但最新版本的gcc(
PS2奖金示例2,非常适合采访;):
double a = 3;
const double & a_ref = a;
const double & x = true ? a_ref : 2.;
a = 4.;
std::cout << a << ", " << x;
Run Code Online (Sandbox Code Playgroud)
输出:
4, 3
Run Code Online (Sandbox Code Playgroud)
首先,条件运算符的结果是指定所选操作数的glvalue,或者其值来自所选操作数的prvalue.
TC注意到的异常:如果至少有一个操作数属于类类型并且具有转换为引用操作符,则结果可以是指定由该操作符的返回值指定的对象的左值; 如果指定的对象实际上是临时的,则可能会产生悬空参考.对于这样的运算符来说,这是一个问题,它提供了prvalues到lvalues的隐式转换,而不是条件运算符本身引入的问题.
在这两种情况下,绑定对结果的引用是安全的,将引用绑定到左值或prvalue的常用规则适用.如果引用绑定到prvalue(条件的prvalue结果,或者从条件的左值结果初始化的prvalue),则prvalue的生命周期被扩展以匹配引用的生命周期.
在您的原始情况下,条件是:
true ? a : 2.
Run Code Online (Sandbox Code Playgroud)
第二个和第三个操作数是:"lvalue of type float
"和"prvalue of type double
".这是cppreference摘要中的案例5,结果是"prvalue of type double
".
然后,您的代码使用不同(非引用相关)类型的prvalue初始化const引用.这样做的行为是复制初始化与引用相同类型的临时.
总之,之后const float & x = true ? a : 2.;
,x
是一个左值表示一个float
其值是转换的结果a
给double
和背部.(不确定是否保证比较等于a
). x
不受约束a
.
在奖励情况1中,条件运算符的第二和第三操作数是"类型的左值float
"和"类型的左值const float
".这是同一个cppreference链接的情况3,
两者都是相同值类别的glvalues,并且除了cv-qualification之外具有相同的类型
行为是第二个操作数被转换为"lvalue of type const float
"(表示相同的对象),条件的结果是"lvalue of type const float
"表示所选对象.
然后绑定const float &
到"lvalue of type const float
",它直接绑定.
所以之后const float & x = true ? a : b;
, x
直接绑定到任何一个a
或b
.
在奖金案例2中,true ? a_ref : 2.
.第二个和第三个操作数是"类型的左值const double
"和"类型的首值double
",因此结果是"类型的首值double
".
然后你绑定它const double & x
,这是一个直接绑定,因为const double
与引用相关double
.
所以在之后const double & x = true ? a_ref : 2.;
,则x
是一个左值,表示一个具有相同值a_ref
(但x
不受约束a
)的double .