为什么不能用参数构造条件中定义的变量?

时间:2011-11-30 20:08:59

标签: c++

问题很简单。为什么编译:

bool b(true);
if (b) { /* */ }

这个编译:

if (bool b = true) { /* */ }

但不是这样:

if (bool b(true)) { /* */ }

在我的实际代码中,我需要构造一个对象并对其进行测试,同时在if-block结束时也将其销毁。基本上,我正在寻找这样的东西:

{
    Dingus dingus(another_dingus);
    if (dingus) {
        // ...
    }
}

当然,这样可行:

if (Dingus dingus = another_dingus) { /* */ }

然后我正在构建Dingus并在其上调用operator=。对我来说似乎合乎逻辑,我能够使用我喜欢的任何构造函数构造对象。

但我很困惑为什么这在语法上不正确。我已经用G ++和MSVC ++进行了测试,他们都抱怨这个结构,所以我确定它是规范的一部分,但我很好奇这个的推理以及可能存在的非丑陋的解决方法。

5 个答案:

答案 0 :(得分:18)

这有点技术性。没有理由不能允许你想要的东西,但事实并非如此。这是语法。

if语句是一个选择语句,它采用语法形式:

if (condition) statement

此处,condition可以是:

  • expression
  • type-specifier-seq declarator = assignment-expression

你有它。在条件允许声明是一种特殊情况,它必须遵循 形式或您的程序格式错误。他们可能可能允许直接初始化而不是复制初始化,但现在没有任何动机。 As Johannes Schaub points out,此更改会破坏现有代码,因此几乎不可能发生。

Let_Me_Be注意到C ++ 11添加了第三种形式(我在这里忽略了属性):

decl-specifier-seq declarator braced-init-list

所以if (bool b{true})很好。 (这不可能破坏任何有效的现有代码。)


注意你的问题似乎与效率有关:别担心。编译器将忽略临时值并直接构造左侧。但是,这需要您的类型可以复制(或在C ++ 11中移动)。

答案 1 :(得分:5)

应该注意的是,if(functor(f)(123)) ...;不再是带有参数123的匿名仿函数的调用,而是会声明由123初始化的仿函数。

我认为为这个小功能引入这样的陷阱是不值得的。


由于上述内容可能尚不清楚,让我们深入了解一下。首先要记住,允许声明符周围的括号,包括直接围绕声明名称的退化情况:

int(n) = 0; 
// same: int n = 0; 

int(n)(0);
// same: int n(0);

两个带括号的版本都不明确,因为第一个可能是赋值,第二个可能是函数调用。但两者也可以是声明。标准说他们声明。

如果我们在条件中允许paren-initializers,那么我们也会将后一种歧义引入条件,就像语句一样。因此,在支持特性之后,我们将生成当前用于声明的有效条件表达式。考虑

typedef bool(*handler_type)(int);

bool f(int) { /* ... */ }
bool f(int, int) { /* ... */ }

void call_it() {
   // user wants to call f(int), but it is overloaded!
   // -> user tries a cast...
   if(handler_type(f)(0)) {
     /* ... */
   }
}

您认为会发生什么?当然,它永远不会进入if主体,因为它总是声明一个空指针。它从不调用函数f。如果没有“功能”,它将正确调用f,因为我们没有歧义。这不仅限于(f),还包括(*f)(声明指针),(&f)(声明引用)等。

再说一次:我们是否想要这样一个小功能的价格陷阱?我不知道有多少人甚至知道他们可以在某种情况下宣布这些东西。

答案 2 :(得分:3)

这是语言语法限制。 if语句中括号中的位可以是表达式,也可以是必须具有以下形式之一的限制形式的声明:

  

attribute-specifier-seq OPT decl-specifier-seq 声明符 = initializer-clause

     

attribute-specifier-seq OPT decl-specifier-seq 声明符 braced-init-list

不允许其他形式的声明。请注意,这里没有分配,只有复制初始化。

如果要在select语句条件下直接初始化对象,则可以使用 braced-init-list 的新形式(自C ++ 11起):

if (Type var { init })
{
    // ...
}

答案 3 :(得分:1)

这是你的问题的解决方案,虽然它没有完全回答这个问题:

if (bool b = bool(true)) { /* */ }

它没有做你认为它正在做的事情 - bool(true)在这种情况下不会调用构造函数,它正在执行转换。例如:

return foo(0);

与:

相同
return static_cast<foo>(0); // or (foo)0

测试:

struct foo {
  foo(int x) {
    std::cout << "ctor\n";
  }
  foo(const foo& x) {
    std::cout << "copy ctor\n";
  }
  operator bool() {
    return true;
  }

};

int main(int, char**) {
  if (foo x = foo(1)) { /* */ }
}

打印“ctor”。由于复制省略,不会调用复制构造函数。

答案 4 :(得分:-3)

因为“=”被定义为一个带有两个参数的函数,并且 - 在执行其作业之后 - 它返回第一个的值。构造函数不返回值。