问题很简单。为什么编译:
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 ++进行了测试,他们都抱怨这个结构,所以我确定它是规范的一部分,但我很好奇这个的推理以及可能存在的非丑陋的解决方法。
答案 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)
因为“=”被定义为一个带有两个参数的函数,并且 - 在执行其作业之后 - 它返回第一个的值。构造函数不返回值。