为什么我不能使用带有 auto 的大括号初始化值并将其传递给此函数

Why can't I initialize a value using braces with auto and pass it into this function

本文关键字:初始化 函数 不能 auto 为什么      更新时间:2023-10-16

为什么我不能用auto初始化一个值,并将其传递给一个需要decltype作为参数的函数

让我来设置场景,并向您展示一个小程序。


这是一个返回值的函数。

int Function(void);

在这种情况下,它恰好是一个整数,但返回类型可能会更改
这就是为什么下一个函数是这样写的:

void What_I_Take_Depends_On_Function(decltype(Function()) x);

如果有人决定更改函数的返回类型,则不需要更改函数的减速。是的,函数的定义可能无法正确处理新类型,或者如果函数的返回类型更改为void,则会出现问题,但这与我的问题无关。

#include <iostream>
#include <cstdlib>
int Function(void){return 5;}
void What_I_Take_Depends_On_Function(decltype(Function()) x){return;}
int main(){
    //assignments(Edit: these are initializations)
    int  var1 = Function();
    auto var2 = Function();
    //initializations
    int  var3 {Function()};
    auto var4 {Function()};
    What_I_Take_Depends_On_Function(var1); //works
    What_I_Take_Depends_On_Function(var2); //works
    What_I_Take_Depends_On_Function(var3); //works
    What_I_Take_Depends_On_Function(var4); //COMPILER ERROR
    //cannot convert ‘std::initializer_list<int>’ to ‘int’ for argument ‘1’ to ‘void What_I_Take_Depends_On_Function(int)’
    return EXIT_SUCCESS;
}

那么,为什么var4是initializer_list而不是int呢
不能自动推断Function将返回int,
然后将减速度改为类似var3的减速度?

大括号初始化列表{...}是C++11的新统一初始化语法,它可以用于初始化任何自动、静态或成员变量,如果该变量的类型已知。以下是有效的C++11初始化的几个例子:

class S
{
private:
  int _m;
public:
  S(int m) : _m { m }  // <== initialization of a member
  {}
};

int main() {
  constexpr unsigned i { 10 }; // <== initialization of a constexpr
  S s { i };  // <== initialization by calling constructor
  /* ... */
}

中,上述任何情况都不会生成std::initializer_list。括号初始化列表{...}只是意味着编译器将尝试为给定的数据类型识别一个合适的构造函数,其参数列表与括号初始化列表的内容匹配(§8.5.4)。

有两种特殊情况:

  1. 如果为给定数据类型定义的构造函数之一std::initializer_list<T>作为参数,并且大括号init列表的内容都可以隐式转换为T。当你使用一种内置的容器类型时,就会出现这种情况,例如

    std::vector<int> vec { 1, 2, 3 };
    

    std::vector<T>有一个构造函数std::vector<T>(const std::initializer_list<T>),因此大括号init列表将被转换为std::initializer_list<int>,并因此被复制到向量构造函数中。构造函数将遍历列表,并逐个追加元素。

    当列表只包含一个参数时,这有点像陷阱:

    std::vector<int> vec { 10 };
    

    在这里,同样的情况也会发生,所以您将得到一个包含一个元素10的向量。这与使用旧式语法不同:

    std::vector<int> vec(10);
    

    这将调用std::vector<int>(const size_t)构造函数,即创建一个包含10个默认初始化元素的向量。

  2. 如果要初始化的变量的类型不是预先确定的,即当声明中使用auto时:

    auto v { 1, 2, 3 };
    

    在这种情况下(§7.1.6.4/6),编译器无法确定合适的构造函数,因为任何采用三个整数(或可转换为整数)参数的数据类型都是可能的候选者。这里的规则是编译器假定std::initializer_list<int>v的数据类型。这也是你的情况。

换句话说,对于初始化来说,使用大括号init列表是可以的(甚至是鼓励的),但您不能很容易地将其与auto结合起来。为了解决您的问题,您需要显式地声明数据类型

int var4 { Function() };

或者,为了保持灵活性,也可以在此处使用decltype

decltype(Function()) var4 { Function() };

或者,您可以使用旧式语法:

auto v (Function());

否,auto不会"只是推断Function将返回int";auto说明符的工作方式在7.1.6.4中进行了定义,如下所示:

(7.1.6.4/p6)一旦根据8.3确定了声明符id的类型,则声明变量的类型使用声明符id是使用模板参数的规则根据其初始值设定项的类型确定的演绎设T是已经为变量标识符d确定的类型。通过以下方式从T获得P用新发明的类型模板参数U替换auto的出现,或者,如果初始值设定项是braced-init-list(8.5.4),具有std::initializer_list<U>。则为变量d推导的类型为使用来自函数调用的模板参数推导规则确定的推导出的CCD_ 26(14.8.1.1),其中CCD_ 27是函数模板参数类型,CCD_。如果扣除失败,申报不正确。

我对语法有误解
我习惯于初始化这样的变量:

int i{0};

但是,我需要用括号而不是括号来初始化这个类型
谢谢Raymond