现代 C++ 中调用 constructor 是习惯使用 (...args) 还是 {..args}

Créé le : 5 septembre 2026

Répondu en utilisant GPT-5.6 Thinking par Chat01

Question

现代 C++ 中调用 constructor 是习惯使用 (...args) 还是 {..args}

Réflexion

Searched 2 websites

Worked for 6s

Réponse

在现代 C++ 里,两种都常见,但我更建议把 () 当作“调用普通 constructor”的默认写法,把 {} 留给你确实想要 list-initialization 语义的时候

原因是 {} 不只是换一种括号,它会改变重载决议:如果类有 std::initializer_list constructor,{} 会优先考虑它。(Cppreference)

最经典的例子:

cpp
std::vector<int> a(10, 20); // 10 个元素,每个都是 20 std::vector<int> b{10, 20}; // 2 个元素:10, 20

所以对于:

cpp
Foo x(arg1, arg2);

我通常会写成这样。它清晰表达:

“我要用 (arg1, arg2) 这组参数做普通 constructor overload resolution。”

而:

cpp
Foo x{arg1, arg2};

表达的是 list-initialization。它有两个重要特性:

  1. 禁止 narrowing conversion,例如:
cpp
int a(3.14); // OK,a == 3 int b{3.14}; // compile error

这是 {} 很大的安全优势。(Cppreference)

  1. initializer_list constructor 有特殊优先级:
cpp
class Foo { public: Foo(int, int); Foo(std::initializer_list<int>); }; Foo a(1, 2); // Foo(int, int) Foo b{1, 2}; // Foo(initializer_list<int>)

因此它们并不是单纯的 stylistic alternatives

我实际推荐的习惯

cpp
// 普通 constructor Foo foo(a, b); // aggregate / struct 数据初始化 Point p{10, 20}; // container 明确列出元素 std::vector<int> xs{1, 2, 3}; // value initialization / zero initialization int n{}; Foo foo{}; // 避免 narrowing 时 SomeType x{value};

尤其是泛型代码,我会明显偏向:

cpp
return T(std::forward<Args>(args)...);

而不是:

cpp
return T{std::forward<Args>(args)...};

因为后者可能意外触发 initializer_list overload,导致泛型代码的语义发生变化。

值得一提的是,C++ Core Guidelines 的 ES.23 确实给出了更强的意见:“Prefer the {}-initializer syntax”,理由包括统一语法、禁止 narrowing,以及避免某些 () 的语法歧义。(ISO C++) 所以你也会看到不少现代 C++ 项目坚持“几乎全部 {}”。

不过工程实践里,initializer_list 的优先级问题使得很多开发者不会无脑遵循“everything braces”。

所以如果要压缩成一个规则:

普通构造用 ();表达“一组值/成员/元素”用 {};需要防 narrowing 时也优先 {}

比如看到:

cpp
Widget w(10, 20); Point p{10, 20}; std::vector<int> v{10, 20};

我会认为这是非常自然、现代而且语义明确的 C++ 风格。

另外,“constructor 本身”的声明当然仍然是 Foo(int x);这里讨论的是对象初始化时选择 () 还是 {}。cppreference 也明确区分 direct-initialization 和 list-initialization。(Cppreference)

Partager cette Q&R