繁体   English   中英

VS 2012中的条件运算符类型转换

[英]Conditional operator type conversion in VS 2012

我目前正在将一个相当大的项目从VS 2008转换到2012年,并且遇到了一个问题,即条件运算符类型转换的执行方式发生了变化。

首先让我说我接受条件运算符的语义有些复杂并且意识到代码原本做的可能不正确,但我真的很困惑现在在VS 2012中发生了什么,我想知道是否有人可以解释为什么它做它做的事情。

class DummyString
{
    wchar_t wchBuf[32];

public:
    DummyString() { *wchBuf = 0; }
    DummyString(int) { *wchBuf = 0; }
    DummyString(const DummyString& ds) { *wchBuf = 0; }

    operator const wchar_t*() const { return wchBuf; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    DummyString ds;
    // note: the argc test is simply to stop the conditional operator
    // being optimised away
    const wchar_t* pPtr = (argc == 100) ? 0 : ds;
    assert(pPtr == static_cast<const wchar_t*>(ds));
    return 0;
}

在VS 2008中,上面的条件运算符将导致在ds上调用operator const wchar_t*()方法,并且不会触发断言。 也就是说,它会隐式地将ds转换为const wchar_t*

在VS 2012中,条件运算符导致以下行为:

  • 通过复制构造函数构造临时DummyString
  • 然后对该临时副本执行对const wchar_t*

这会导致pPtr指向被破坏的对象,并且当然会触发。

现在,如果我从类中删除DummyString(int)构造函数,代码无法在VS2012中编译( 没有从'DummyString'转换为'int' ),所以条件中的0显然导致表达式被评估为int而不是一个指针。

但在这种情况下,为什么不DummyString(int)构造函数将0转换为DummyString 为什么编译器会创建ds的副本然后将其转换为wchar_t *,何时可以轻松地对原始对象执行强制转换?

我很想得到开悟! :)

C ++ 11标准第5.16 / 3段规定:

[...]如果第二个和第三个操作数具有不同的类型并且具有(可能是cv限定的)类类型,或者如果两者都是相同值类别的glvalues且除了cv-qualification之外的相同类型, 则尝试将每个操作数转换为另一个操作数的类型 [...]

然后:

[...]如果两者都可以转换,或者一个可以转换,但转换含糊不清,则程序格式错误。 如果两者都不能被转换,则操作数保持不变并且如下所述执行进一步检查。 如果只能进行一次转换,则将该转换应用于所选操作数,并使用转换后的操作数代替本节其余部分的原始操作数。

在这种情况下,只有从intDummyString的转换是可能的,因为在另一个方向上, const wchar_t*是我们可以去的 - 没有从const wchar_t*int标准隐式转换。

这就是为什么编译器会抱怨如果删除带有int的转换构造函数:如果该构造函数不存在,则根据上面的段落,程序将是格式错误的(在任一方向都不存在转换)。

因此,第二个操作数(第一个选项)被认为是DummyString(0)

但是,第二个操作数可以转换为DummyString事实并不意味着将根据第二个操作数进行评估。 这取决于条件,并且条件求值为false (除非您将100个参数传递给命令行),这解释了为什么您没有看到对该构造函数的调用。 根据第5.16 / 1段:

条件表达式从右到左分组。 第一个表达式在上下文中转换为bool(第4条)。 它被评估,如果是,则条件表达式的结果是第二个表达式的值,否则是第三个表达式的值。 仅评估第二和第三表达式中的一个。 [...]

但是为什么你的断言失败呢?

为什么编译器会创建ds的副本然后将其转换为wchar_t* ,何时可以轻松地对原始对象执行强制转换?

那么,这是由于第5.16 / 4-5段:

如果第二个和第三个操作数是相同值类别的glvalues并且具有相同的类型,[...]

否则,结果是prvalue 如果第二个和第三个操作数不具有相同的类型,并且具有(可能是cv限定的)类类型,则使用重载决策来确定要应用于操作数的转换(如果有)(13.3.1.2,13.6) 。

0不是glvalue,因此条件的结果将是prvalue。 这意味着当条件为false时对条件运算符的求值最终会从ds构造一个临时值 ,这是您观察到的行为。

这是第5.16 / 6段规定的,其中说:

在第二个和第三个操作数上执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换。 完成转换后,以下其中一项应成立:

- 第二个和第三个操作数具有相同的类型; 结果是那种类型。 如果操作数具有类类型,则结果是结果类型的prvalue临时值,它根据第一个操作数的值从第二个操作数或第三个操作数进行复制初始化 [...]

条件“” 第二个和第三个操作数具有相同的类型 “成立”,因为在5.16 / 3中描述的转换之后现在正在考虑操作数(参见本答案的开头)。

要解决您的问题,您可以对第二个参数执行显式强制转换:

const wchar_t* pPtr = (argc == 100) ? 0 : static_cast<const wchar_t*>(ds);

由于存在从0到指针类型的标准转换并导致空指针 - 请参阅第4.10 / 1段。

如果需要,可以通过以下方式避免调用复制构造函数:

const wchar_t* pPtr = (argc == 100) ? 0 : (pPtr = ds);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM