1

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

让我设置场景,给你看一个小程序。


这是一个返回值的函数。

int Function(void);

在这种情况下,它恰好是一个整数,但返回类型可能会发生变化。
这就是为什么下一个函数编写如下:

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

如果有人决定改变 Function 的返回类型,那么这个函数的减速就不需要改变了。是的,函数的定义可能无法正确处理新类型,或者如果函数的返回类型更改为 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 呢?
不能 auto 只是推断 Function 将返回一个 int,
然后将减速更改为类似于 var3 的减速吗?

4

3 回答 3

3

大括号初始化列表是 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不会生成 a。大括号初始化列表{...}仅意味着编译器将尝试为给定数据类型识别合适的构造函数,其参数列表与大括号初始化列表的内容匹配(第 8.5.4 节)。

两种特殊情况:

  1. 如果为给定数据类型定义的构造函数之一将 astd::initializer_list<T>作为参数,并且大括号初始化列表的内容都可以隐式转换为T. 当您使用其中一种内置容器类型时就是这种情况,例如

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

    std::vector<T>有一个构造函数std::vector<T>(const std::initializer_list<T>),因此大括号初始化列表将被转换为 astd::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. 这也是你的情况。

换句话说,使用大括号初始化列表很好(甚至鼓励)初始化,但你不能轻易地将它与auto. 要解决您的问题,您需要明确声明数据类型

int var4 { Function() };

或者,为了保持灵活性,也可以decltype在这里使用:

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

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

auto v (Function());
于 2012-09-02T01:06:09.003 回答
2

不,auto不会“只是推断 Function 将返回一个 int”;auto说明符的工作方式在 7.1.6.4 中有明确定义,如下所示:

(7.1.6.4/p6) 一旦根据 8.3 确定了declarator-id的类型,则使用declarator-id声明的变量的类型由使用模板参数推导规则的初始值设定项的类型确定。让T是已为变量标识符确定的类型d。通过PT的 替换为auto新发明的类型模板参数U,或者,如果初始值设定项是braced-init-list(8.5.4),则使用std::initializer_list<U>. d然后A使用函数调用 (14.8.2.1) 中的模板参数推导规则确定为变量推导的类型,其中P是一个函数模板参数类型,初始化器d是对应的参数。如果扣除失败,则声明格式错误。

于 2012-09-01T04:03:25.510 回答
0

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

int i{0};

但是,我需要用括号而不是括号来初始化这种类型。
谢谢雷蒙德

于 2012-09-01T05:05:50.090 回答