You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中使用圆括号调用拷贝构造函数为何触发编译错误

问题现象

你实现了一个包含自定义拷贝构造、移动构造的类A,代码如下:

#include <cstring>
#include <utility>
class A
{
  private:
  int        *pa_;
  std::size_t size_;

  public:
  explicit A(std::size_t size)
      : pa_{new int[size]}, size_{size} {};

  // copy constructor
  A(const A &other)
      : pa_{new int[other.size_]}, size_{other.size_}
  {
    std::memcpy(pa_, other.pa_, size_);
  }

  // move constructor
  A(A &&other)
      : pa_{other.pa_}, size_{other.size_}
  {
    other.pa_ = nullptr;
  }
};

编写测试代码验证构造函数调用行为时,发现独立语句中使用圆括号调用拷贝构造会触发编译错误,测试代码如下:

int main(int argc, char const *argv[])
{
  A a1{100};    // ok
  A(a1);        // error
  A a2 = A(a1); // ok
  A{a1};        // ok
  A aa[] = {    // ok
            A(a1),
            A(a1)};
  A(std::move(a1)); // ok
}

使用g++-11 -std=c++17 -Wall -pedantic-errors编译,输出错误信息如下:

example.cpp: In function 'int main(int, const char**)':
example.cpp:30:4: warning: unnecessary parentheses in declaration of 'a1' [-Wparentheses]
   30 |   A(a1);        // error
      |    ^~~~
example.cpp:30:4: note: remove parentheses
   30 |   A(a1);        // error
      |    ^~~~
      |    -  -
example.cpp:30:5: error: redeclaration of 'A a1'
   30 |   A(a1);        // error
      |     ^~
example.cpp:29:5: note: 'A a1' previously declared here
   29 |   A a1{100};    // ok
      |     ^~
example.cpp:33:5: warning: unused variable 'aa' [-Wunused-variable]
   33 |   A aa[] = {    // ok
      |     ^~
根本原因

这个反直觉的行为来自C++标准规定的歧义解析规则,也就是社区常说的最令人头疼的解析(Most Vexing Parse):

  • 当一段代码同时符合「变量声明」和「创建临时对象的表达式语句」两种语法规则时,编译器必须优先将其解析为变量声明。
  • 你写的A(a1);不会被识别为「调用拷贝构造生成A类型临时对象」,而是会被解析为声明一个名为a1的A类型变量,包裹a1的圆括号在声明语法中是冗余合法的,整句等价于A a1;。
  • 上一行代码已经声明过同名变量A a1{100};,同一作用域下重复声明同名变量自然触发重定义错误,编译器输出的「a1声明存在不必要括号」的警告也完全对应这个解析逻辑。

其余写法能正常工作的原因也很明确:

  • A a2 = A(a1);中A(a1)位于初始化表达式的右值位置,不存在被解析为独立声明的歧义,会被正确识别为临时对象构造。
  • A{a1};使用花括号列表初始化语法,C++标准明确规定该形式不会触发上述歧义解析,会直接识别为临时对象构造。
  • 数组初始化列表内的A(a1)、独立语句中的A(std::move(a1)),括号内分别是初始化子句、带作用域限定的函数调用表达式,不可能被解析为变量名,因此不存在歧义,会正常触发拷贝/移动构造。
解决方案

如果确实需要在独立语句中使用圆括号形式构造临时对象,可以通过以下写法消除歧义:

  • 在外层额外包裹一层括号,让语句不符合声明语法规则:
    (A(a1)); // 无法被解析为变量声明,会正确调用拷贝构造生成临时对象
    
  • 直接使用花括号初始化形式,从语法根源避免歧义:
    A{a1}; // 无歧义构造临时对象
    

日常开发中,C++11及以后版本推荐优先使用花括号初始化,可以规避绝大多数这类解析歧义问题。

内容的提问来源于stack exchange,提问作者ph3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 22:33:23