在 C++ 中初始化变量的正确方法是什么
What is the correct way to initialize a variable in C++
我有以下代码:
bool c (a == b);
和
bool c {a == b};
其中 a 和 b 是一些相同类型的变量。
我想知道,以上两种初始化有什么区别,在什么情况下应该首选哪一种?任何形式的帮助将不胜感激。
两种形式都是direct initialization.
使用大括号 {}
进行初始化检查缩小转换,如果发生此类转换,则会生成错误。不像 ()
。 (gcc issues a warning by default 并且需要 -Werror=narrowing
编译器选项以在发生缩小时生成错误。)
大括号{}
的另一个用途是统一初始化:使用相同的语法初始化有和没有构造函数的两种类型,例如:
template<class T, class... Args>
T create(Args&&... args) {
T value{std::forward<Args>(args)...}; // <--- uniform initialization + perfect forwarding
return value;
}
struct X { int a, b; };
struct Y { Y(int, int, int); };
int main() {
auto x = create<X>(1, 2); // POD
auto y = create<Y>(1, 2, 3); // A class with a constructor.
auto z = create<int>(1); // built-in type
}
使用花括号 {}
进行初始化的唯一缺点是它与 auto
关键字的交互。 auto
将 {}
推断为 std::initializer_list
,这是一个已知问题,请参阅 "Auto and braced-init-lists"。
第一个是C++03风格的直接初始化。
第二种是 C++11 风格的直接初始化,它额外检查缩小转换。 Herb Sutter 在新代码中推荐以下内容:
auto c = <expression>;
或者当你想提交特定类型 T:
auto c = T{<expression>};
当 T 是一些 class 且构造函数重载时,花括号的一个已知缺点,其中一个构造函数获取 std::initializer_list 作为参数,例如 std::vector:
auto v = std::vector<int>{10}; // create vector<int> with one element = 10
auto v = std::vector<int>(10); // create vector<int> with 10 integer elements
现在我们有五种初始化形式。他们是
T x = expression;
T x = ( expression );
T x ( expression );
T x = { expression };
T x { expression };
每种形式都有自己的特点。 :)
例如,假设您在全局命名空间中有以下声明
int x;
void f( int x ) { ::x = x; }
int g() { return x ; }
long h() { return x; }
然后在main中你可以写
int main()
{
int x ( g() );
}
此代码将编译成功。
然而程序员误打错了
int main()
{
int x; ( g() );
^^
}
糟糕!此代码也编译成功。:)
但是如果程序员会写
int main()
{
int x = ( g() );
}
然后打错字
int main()
{
int x; = ( g() );
^^
}
那么在这种情况下代码将无法编译。
好吧,假设程序员首先决定在初始化局部变量之前为全局变量 x 设置一个新值。
于是他写了
int main()
{
int x ( f( 10 ), g() );
}
但是这段代码无法编译!
让我们插入等号
int main()
{
int x = ( f( 10 ), g() );
}
现在代码编译成功!
那么牙套呢?
这两个代码都不是
int main()
{
int x { f( 10 ), g() };
}
也不是这个代码
int main()
{
int x = { f( 10 ), g() };
}
编译!:)
现在程序员决定使用函数 h(),他写了
int main()
{
int x ( h() );
}
并且他的代码编译成功。但过了一段时间他决定使用牙套
int main()
{
int x { h() };
}
糟糕!他的编译器报错
error: non-constant-expression cannot be narrowed from type 'long' to
'int' in initializer list
程序决定使用类型说明符 auto。他尝试了两种方法
int main()
{
auto x { 10 };
x = 20;
}
和
int main()
{
auto x = { 10 };
x = 20;
}
并且...有些编译器编译了第一个程序但没有编译第二个程序,有些编译器没有编译这两个程序。:)
那么使用 decltype
呢?
比如程序员写的
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = a;
}
而他的编译器报错了!
但是当程序员像这样把a括在括号里时
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = ( a );
}
代码编译成功!:)
现在程序员决定学习OOP。他写了一个简单的class
struct Int
{
Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
并且他的代码编译成功。
但是程序员已经知道有函数说明符 explicit
并且他决定使用它
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
糟糕!他的编译器报错
error: chosen constructor is explicit in copy-initialization
程序员决定去掉赋值符号
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x { 10 };
}
他的代码编译成功!:)
我有以下代码:
bool c (a == b);
和
bool c {a == b};
其中 a 和 b 是一些相同类型的变量。
我想知道,以上两种初始化有什么区别,在什么情况下应该首选哪一种?任何形式的帮助将不胜感激。
两种形式都是direct initialization.
使用大括号 {}
进行初始化检查缩小转换,如果发生此类转换,则会生成错误。不像 ()
。 (gcc issues a warning by default 并且需要 -Werror=narrowing
编译器选项以在发生缩小时生成错误。)
大括号{}
的另一个用途是统一初始化:使用相同的语法初始化有和没有构造函数的两种类型,例如:
template<class T, class... Args>
T create(Args&&... args) {
T value{std::forward<Args>(args)...}; // <--- uniform initialization + perfect forwarding
return value;
}
struct X { int a, b; };
struct Y { Y(int, int, int); };
int main() {
auto x = create<X>(1, 2); // POD
auto y = create<Y>(1, 2, 3); // A class with a constructor.
auto z = create<int>(1); // built-in type
}
使用花括号 {}
进行初始化的唯一缺点是它与 auto
关键字的交互。 auto
将 {}
推断为 std::initializer_list
,这是一个已知问题,请参阅 "Auto and braced-init-lists"。
第一个是C++03风格的直接初始化。 第二种是 C++11 风格的直接初始化,它额外检查缩小转换。 Herb Sutter 在新代码中推荐以下内容:
auto c = <expression>;
或者当你想提交特定类型 T:
auto c = T{<expression>};
当 T 是一些 class 且构造函数重载时,花括号的一个已知缺点,其中一个构造函数获取 std::initializer_list 作为参数,例如 std::vector:
auto v = std::vector<int>{10}; // create vector<int> with one element = 10
auto v = std::vector<int>(10); // create vector<int> with 10 integer elements
现在我们有五种初始化形式。他们是
T x = expression;
T x = ( expression );
T x ( expression );
T x = { expression };
T x { expression };
每种形式都有自己的特点。 :)
例如,假设您在全局命名空间中有以下声明
int x;
void f( int x ) { ::x = x; }
int g() { return x ; }
long h() { return x; }
然后在main中你可以写
int main()
{
int x ( g() );
}
此代码将编译成功。
然而程序员误打错了
int main()
{
int x; ( g() );
^^
}
糟糕!此代码也编译成功。:)
但是如果程序员会写
int main()
{
int x = ( g() );
}
然后打错字
int main()
{
int x; = ( g() );
^^
}
那么在这种情况下代码将无法编译。
好吧,假设程序员首先决定在初始化局部变量之前为全局变量 x 设置一个新值。
于是他写了
int main()
{
int x ( f( 10 ), g() );
}
但是这段代码无法编译!
让我们插入等号
int main()
{
int x = ( f( 10 ), g() );
}
现在代码编译成功!
那么牙套呢?
这两个代码都不是
int main()
{
int x { f( 10 ), g() };
}
也不是这个代码
int main()
{
int x = { f( 10 ), g() };
}
编译!:)
现在程序员决定使用函数 h(),他写了
int main()
{
int x ( h() );
}
并且他的代码编译成功。但过了一段时间他决定使用牙套
int main()
{
int x { h() };
}
糟糕!他的编译器报错
error: non-constant-expression cannot be narrowed from type 'long' to 'int' in initializer list
程序决定使用类型说明符 auto。他尝试了两种方法
int main()
{
auto x { 10 };
x = 20;
}
和
int main()
{
auto x = { 10 };
x = 20;
}
并且...有些编译器编译了第一个程序但没有编译第二个程序,有些编译器没有编译这两个程序。:)
那么使用 decltype
呢?
比如程序员写的
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = a;
}
而他的编译器报错了!
但是当程序员像这样把a括在括号里时
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = ( a );
}
代码编译成功!:)
现在程序员决定学习OOP。他写了一个简单的class
struct Int
{
Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
并且他的代码编译成功。
但是程序员已经知道有函数说明符 explicit
并且他决定使用它
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
糟糕!他的编译器报错
error: chosen constructor is explicit in copy-initialization
程序员决定去掉赋值符号
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x { 10 };
}
他的代码编译成功!:)