为何编译器报错A c{}.foo()?如何避免赋值操作?
class A { public: A(void) = default; A& foo(void) { return *this; } }; int main(int, char**) { A a = A{}.foo(); A b{}; A c{}.foo(); return 0; }
编译错误输出:
C:\temp\cpp>clang test.cpp test.cpp:15:8: error: expected ';' at end of declaration 15 | A c{}.foo(); | ^ | ; 1 error generated.
问题解答
1. 编译器为何会对语句A c{}.foo()报错?
这是C语法规则的限制:A c{}本身是一条变量声明语句,用来创建名为c的A类型对象并完成初始化。但在声明语句的初始化器后直接追加.foo()调用的写法,不符合C的声明语法规范——编译器会认为A c{}已经是完整的声明,后续的.foo()属于多余的非法语法,因此要求在{}后加分号来终止声明语句。
简单来说:C++不允许将变量声明和成员函数调用链式写在一起,这种写法混淆了声明语句和表达式语句的语法边界。
2. 是否存在可以避免使用赋值操作的实现方式?
存在,且能适配禁用拷贝构造函数的场景,以下是几种可行方案:
方案1:利用右值引用转移临时对象所有权(C++11及以上)
如果类A允许移动构造(默认生成或显式声明),可以通过std::move将临时对象的所有权转移给目标变量,完全避免拷贝操作:class A { public: A(void) = default; A(const A&) = delete; // 禁用拷贝构造 A(A&&) = default; // 启用移动构造 A& foo(void) { return *this; } }; int main() { A c = std::move(A{}.foo()); // 通过move转移临时对象,无拷贝 return 0; }方案2:静态工厂函数+C++17 NRVO优化
给类A添加静态工厂函数,在函数内部完成对象初始化和foo()调用,C++17的NRVO(命名返回值优化)会直接在目标变量的内存空间构造对象,无需拷贝或移动:class A { public: A(void) = default; A(const A&) = delete; A(A&&) = default; A& foo(void) { return *this; } static A create_and_foo() { A a; a.foo(); return a; } }; int main() { A c = A::create_and_foo(); // C++17及以上无拷贝/移动操作 return 0; }方案3:lambda表达式直接初始化
用lambda表达式封装对象创建和函数调用的逻辑,返回右值对象初始化变量,同样无需拷贝:int main() { A c([]{ A a; a.foo(); return std::move(a); }()); return 0; }
内容的提问来源于stack exchange,提问作者albert
相关产品推荐
相关产品推荐

