使用条件if else运算符初始化引用变量

12
以下C++代码是无效的,因为引用变量需要初始化器:
int& a; // illegal
if (isfive) {
  a = 5;
} else {
  a = 4;
}

然而,MSVC似乎认为这是可以的:

int& a = isfive ? 5 : 4;

根据我的理解,MSVC 实际上将条件运算符视为一个单独的表达式,而不是将其展开为 if-else 语句。

在 C++ 中,使用条件运算符初始化引用是否总是有效的?


我很好奇如果你尝试在汇编级别进行比较会发生什么... - 0x1F602
4
你如何在汇编级别比较编译后的代码和未编译的代码? - Michael Krelin - hacker
1
@MichaelKrelin:不幸的是,在Visual C++中,这种非法代码是被接受的。(第二种形式)。 - Ben Voigt
3
所以我们现在有一些无法编译的代码,还有更多无法编译的代码,我同意很难比较它们的汇编结果。 - Ben Voigt
1
@krynr,你是指第二个吗?引用是临时的,我想。我不知道它有多短暂...其实我认为OP并不是指两个常量,他可能在谈论条件语句,并且给出了一个不幸的例子。 - Michael Krelin - hacker
显示剩余3条评论
7个回答

15

三元运算符不会扩展为if-else结构(至少在语言层面上不会,虽然实现可能会生成等效的二进制代码)。因此,以下代码是有效的:

int four = 4, five = 5;
int& r = condition? four : five;

问题中的原始示例依赖于微软的扩展,它(不正确地)允许将非const引用绑定到rvalue表达式。


喜欢它。不确定为什么我没想到这个。我写了一个函数等等。 - Pavithran Ravichandiran

5

MSVC有一个非标准的“扩展功能”,意思是它允许错误的代码,这是被禁止的很好的理由。

请注意:

int& a = 5;

在标准C++中,这也是不合法的。

但是通常情况下,使用任何可以转换为正确类型的表达式(包括使用条件运算符)都可以用于初始化const引用。此外,当某些条件满足时,使用正确类型的左值初始化非const引用,这也是合法的。


2
哇,你知道吗,问这个问题解决了我甚至不知道自己有的问题。谢谢! - Kai

5

你发布的代码无法在VC++ 2010中编译:

错误 1 错误 C2440: 'initializing' : 无法将'int'转换为'int &'

将该行改为:

const int& a = isfive ? 5 : 4; 

使其编译。

我假设你正在使用/Za(禁用语言扩展)选项? - Ben Voigt
@BenVoigt:不是的。看起来问题已经解决了:http://technet.microsoft.com/en-us/query/szywdw8k - Nemanja Trifunovic
我想知道为什么这个在TechNet上而不是MSDN上。还有一些相关的bug,但很高兴看到最典型的情况已经被修复了。 - Ben Voigt

3

条件运算符是一个表达式,而不是语句。像这样初始化引用非常正常。这有点像通过调用函数初始化引用。

请注意,如果将引用绑定到临时对象,则需要将其声明为const(这是MSVC++愚蠢地忽略的规则)。


如果我们忽略不能将非const引用绑定到rvalue的事实,那么像这样初始化引用是完全可以的(即const int& r = isfive? 4 : 5;是可以的,但int& r = isfive? 4 : 5;不行)。 - David Rodríguez - dribeas
@DavidRodríguez-dribeas 是的,我一段时间前添加了一条关于这个的注释。 - Seth Carnegie

1

这不行

int& a = isfive ? 5 : 4;

除非您将引用“a”声明为const


1

指针+引用技术

感觉这是C++语言的一个限制,可以通过引入新的语言特性来克服。

在那之前,我认为最有效的选择(除非您在if else中执行的操作不仅仅是设置引用,在这种情况下,您可以使用三元运算符?)是使用指针,然后将引用设置为指针,以避免在稍后每次使用变量时进行多个解引用:

main.cpp

#include <iostream>

int main(int argc, char **argv) {
    (void)argv;
    int x = 1;
    int y = 2;
    int *ptr;
    if (argc > 1) {
        ptr = &x;
    } else {
        ptr = &y;
    }
    // One pointer dereference here, I don't see how to get rid of this.
    int &z = *ptr;
    // No pointer dereference on any of the below usages.
    z = 3;
    std::cout << x << std::endl;
    std::cout << y << std::endl;
    std::cout << z << std::endl;
}

编译:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp

运行:

./main.out

输出:

1
3
3

再次运行:

./main.out a

输出:

3
2
3

如果在编译时已知 if 条件,你当然可以使用预处理器或者更好的 C++17 的 if constexpr

相关链接:C++ 中如何在编译时进行 if / else 判断?

相关链接:如何先声明引用再进行初始化?


1
你可以通过lambda表达式来避免指针/解引用的使用:auto& ref = [&]() -> int& { if (cond) return x; return y;}(); - Quest

0

它是运算符,表达式的一部分,而不是语句。即使只是短暂的时间,你也不能让引用未初始化 ;-)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接