{}和等号变量之间的差异

Nic*_*ick 6 c++ variables for-loop initialization list-initialization

我是C ++编程的新手。我在Google的任何地方都找不到我的答案,因此希望可以在这里回答。

以下之间有区别吗

unsigned int counter{ 1 };
Run Code Online (Sandbox Code Playgroud)

要么

unsigned int counter = 1;
Run Code Online (Sandbox Code Playgroud)

这本书使用了第一种选择,它使我感到困惑,因为它没有解释差异。以下是我正在关注的书中的以下代码。

#include <iostream>
#include <iomanip>
#include <cstdlib> // contains function prototype for rand()
using namespace std;

int main()
{
    for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
        cout << setw(10) << (1 + rand() % 6);

        // if counter is divisible by 5, start a new line of output
        if (counter % 5 == 0) {
            cout << endl;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Bia*_*sta 5

是的,它们是C ++中的两种不同类型的初始化。

有关所有详细信息,您可以直接参考文档。

但是,我可以强调:

复制初始化比直接初始化要宽松:显式构造函数不会转换构造函数,因此不会考虑进行复制初始化。

初始化参考在这里

对于unsigned int类型(例如您的情况),两个初始化之间没有真正的区别。


注意 第一个语句(unsigned int counter{ 1 })中花括号的使用提供了附加的约束:

否则(如果T不是类类型),如果braced-init-list仅具有一个元素[...],则T被直接初始化,除非不允许缩小转换

换句话说,在初始化中使用花括号不允许出现数据松动。

那是:

unsigned int counter{ 12.3 };  // error!!!
Run Code Online (Sandbox Code Playgroud)

无法编译,因为您尝试使用浮点值初始化整数。

请注意,这是初始化中花括号的“属性”。它与初始化类型不严格相关

实际上,您还可以编写:

unsigned int counter = { 12.3 };  // error!
Run Code Online (Sandbox Code Playgroud)

相反,它是一个副本初始化,但使用花括号不允许缩小转换范围。


Vla*_*cow 1

考虑以下演示程序。

#include <iostream>

struct A
{
    int x;
    explicit A( int x = 0 ) : x( x ) {}
};

int main()
{
    A a1 { 10 };
    std::cout << "a1.x = " << a1.x << '\n';

//    A a2 = { 10 };
}
Run Code Online (Sandbox Code Playgroud)

在这份声明中

A a1 { 10 };
Run Code Online (Sandbox Code Playgroud)

这里使用的是直接初始化。

并在评论声明中

//    A a2 = { 10 };
Run Code Online (Sandbox Code Playgroud)

也可以重写为

//    A a2 = 10;
Run Code Online (Sandbox Code Playgroud)

这里使用了复制初始化。但构造函数是用说明符声明的explicit。所以编译器会报错。也就是说它无法将整数对象隐式转换10为类型A

你可以写

    A a2 = A{ 10 };
Run Code Online (Sandbox Code Playgroud)

那就是显式调用构造函数。

当然,对于基本类型没有区别,因为除了使用大括号初始化时不允许缩小转换之外,都没有应用构造函数,例如

int x { 1.0 };
Run Code Online (Sandbox Code Playgroud)

当类型说明符是占位符时,有很大的区别auto

例如

auto x = 10;
Run Code Online (Sandbox Code Playgroud)

x有类型int.

auto x { 10 };
Run Code Online (Sandbox Code Playgroud)

x又有型int

auto x = { 10 };
Run Code Online (Sandbox Code Playgroud)

现在x有类型std::initializer_list<int>

例如你可以重写你的循环

for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
Run Code Online (Sandbox Code Playgroud)

以下方式

for ( auto counter{ 1u }; counter <= 20; ++counter) {
Run Code Online (Sandbox Code Playgroud)

但你可能不会写

for ( auto counter = { 1u }; counter <= 20; ++counter) {
Run Code Online (Sandbox Code Playgroud)

因为在这种情况下变量的类型counterstd::initializer_list<unsigned int>.

所以一般来说你有以下形式的初始化

T x = value;
T x = { value };
T x( value );
T x { value };
Run Code Online (Sandbox Code Playgroud)

例如

#include <iostream>

int main()
{
    int x1 = 1;

    std::cout << "x1 = " << x1 << '\n';

    int x2 = ( 2 );

    std::cout << "x2 = " << x2 << '\n';

    int x3( 3 );

    std::cout << "x3 = " << x3 << '\n';

    int x4{ 4 };

    std::cout << "x4 = " << x4 << '\n';
}    
Run Code Online (Sandbox Code Playgroud)

程序输出是

x1 = 1
x2 = 2
x3 = 3
x4 = 4
Run Code Online (Sandbox Code Playgroud)

但还有一种情况,T()您应该将其用作T{}初始化器。这是使用模板函数的时候。

考虑以下示范程序

#include <iostream>

template <class>
void f()
{
    std::cout << "f<T>() is called\n";
}

template <int>
void f()
{
    std::cout << "f<int>() is called\n";
}

int main()
{
    f<int()>();
    f<int{}>();
}    
Run Code Online (Sandbox Code Playgroud)

它的输出是

f<T>() is called
f<int>() is called
Run Code Online (Sandbox Code Playgroud)

int()用作模板参数的构造指定类型模板参数,int而用作模板参数指定的构造则指定类型等于int{}的非类型模板参数。int0