【问题标题】:What is the type of an 'auto' return type when returning *this in an anonymous class?在匿名类中返回 *this 时,“自动”返回类型的类型是什么?
【发布时间】:2020-10-29 17:47:08
【问题描述】:

在这段代码中:

struct
{
    auto operator[](const char*)
    {
        return *this;
    }

} m_some_class;

这里auto的类型是什么?

【问题讨论】:

  • Auto 告诉编译器根据return 语句计算出返回类型。
  • auto 显然是对象的类型。

标签: c++ class c++11 auto


【解决方案1】:

这里的auto 是什么类型?

类型为decltype(m_some_class) - 即返回值与变量m_some_class的类型相同。


请注意,该函数将返回*this副本

如果需要引用 *this,您可以使用 auto& 或者,从 C++14 开始,更通用的 decltype(auto)

【讨论】:

  • 值得明确指出:“这个类没有标识符(名称)。这种情况很少见,但确实会发生。Lambdas 也创建没有名称的类,并且官方 nullptr 没有命名类型”跨度>
  • @MooingDuck 没有命名类型,真的吗? static_assert(std::is_same_v<std::nullptr_t,decltype(nullptr)>); 编译得很好,所以 std::nullptr_tnullptr 的命名类型。
  • @MooingDuck 如果我没记错的话,nullptrnullptr_t)的命名类型是decltype(nullptr)。 @Ruslan:我认为这实际上是它的定义。它倒退了——我喜欢这样。 :)
  • @Ruslan:如果我记得,nullptr 是一个关键字,而不是一个标识符(否则你必须在标题中包含它)。 nullptr_tdecltype(nullptr) 的 typedef 定义在 <cstddef> en.cppreference.com/w/cpp/language/nullptr
【解决方案2】:

对于匿名结构类型,编译器在内部创建一个名称,您的情况下的自动返回结构。

您可以在下面看到,您的匿名结构的名称为 __anon_1_1operator[] 函数返回 __anon_1_1 结构的对象。 m_some_class__anon_1_1 类型的实例

cppinsights网站提供了一种理解方式

你的代码

struct
{
    auto operator[](const char*)
    {
        return *this;
    }

}m_some_class;

编译器版本

struct __anon_1_1
{
  inline __anon_1_1 operator[](const char *)
  {
    return __anon_1_1(*this);
  }
  
  // inline constexpr __anon_1_1() noexcept = default;
  // inline constexpr __anon_1_1(const __anon_1_1 &) noexcept = default;
};

__anon_1_1 m_some_class = __anon_1_1();

【讨论】:

  • 不清楚这 实际上 是内部使用的名称,而不是诊断的人工制品(必须生成 some 名称以避免错误消息中出现空白)。在实践中几乎没有理由不这样做,但这就是为什么我们不做假设而只是按照标准编写代码(即认为类型没有没有名称)。
【解决方案3】:

给定代码中的行:

return *this;

返回结构体m_some_class本身,即operator[]的类型为:

decltype(m_some_class); // i.e. the type returned is the same as the struct

另外,请注意,这只会返回结构的副本实例,因为传递的参数没有给出任何引用运算符。对结构副本所做的任何更改都不会影响原始结构。


auto 关键字是什么?

auto 关键字通常用于程序员不知道某物的类型或输入太长的情况。

此外,auto 定义的类型可能会因各种情况而异。例如:

auto len = vector.size(); // len is now defined as size_t in compile time

在某些系统中,len 的类型可能是 unsigned long,而在我的情况下,它是 unsigned long long,在这里您无法明确定义在这个不确定的地方正确使用哪个限定符。这里我们使用auto关键字。

【讨论】:

  • OP 似乎知道auto 返回机制是什么。问题是,anonymous 类/结构的类型是什么。程序员不可能编写该类型(除非 Ted 提到使用decltype。(另请注意,类型不是m_some_class,这是匿名结构的对象)
  • @TedLyngmo 现在看看。阅读参考资料后,我已经更新了答案。
  • @RohanBari 更清晰! +1
  • @TedLyngmo 谢谢你,我也刚刚听说过decltype(),但几分钟前就明白了它的用法。谢谢你指导我。
  • @RohanBari 欢迎您。在这种特殊情况下,可以创建一个type aliasusing foo_t = decltype(m_some_class);,然后声明该函数返回一个foo_t。 :-)
【解决方案4】:

以下所有标准参考均指N4659: March 2017 post-Kona working draft/C++17 DIS


TLDR:占位符返回类型推导

类是匿名的并不重要,因为返回类型仅从return 语句推导出来。

// Denote the type of the anonymous class as 'T'.

// Return type deduced to 'T'
auto operator[](const char*)
{
    return *this;
}

// Return type deduced to 'T&'
auto& operator[](const char*)
{
    return *this;
}

// Return type deduced to 'T&'
decltype(auto) operator[](const char*)
{
    return *this;
}

详情及相关标准段落如下。


占位符返回类型推导:auto(C++11 及更高版本)

来自[expr.unary.op]/1 [摘录,强调我的]:

[expr.unary.op]/1 一元* 运算符执行间接:应用它的表达式应该是指向一个 对象类型,或指向函数类型的指针,结果是 左值引用表达式所指向的对象或函数 点。 [...]

因此,*this 的结果是一个左值,指向调用运算符调用的对象。

来自[dcl.spec.auto]/1[dcl.spec.auto]/2 [摘录,强调我的]:

[dcl.spec.auto]/1 autodecltype(auto) type-specifiers 用于指定占位符类型 稍后由初始化程序的推导替换。 [...]

[dcl.spec.auto]/2 占位符类型可以与函数声明符一起出现 [...] 在这样的声明符所在的任何上下文中 有效的。 [...] 如果函数声明的返回类型包含一个 占位符类型,函数的返回类型推导自 未丢弃的return 语句 [...]。

来自[dcl.type.auto.deduct]/2[dcl.type.auto.deduct]/4 [摘录,强调我的]:

[dcl.type.auto.deduct]/2 包含占位符类型的类型T,以及对应的初始化器e,确定如下:

  • (2.1) 用于未丢​​弃的return 语句,该语句出现在使用包含占位符类型的返回类型声明的函数中, T 是声明的返回类型,e 是返回的操作数 陈述。如果 return 语句没有操作数,那么 evoid();
  • [...]

[dcl.spec.auto]/4如果占位符是自动类型说明符,则推导的类型T'替换T使用规则确定 用于模板参数推导。 [...]

[ 示例:

const auto &i = expr;

i的类型是调用中参数u的推导类型 f(expr) 以下发明的函数模板:

template <class U> void f(const U& u);

 — 结束示例 ]

因此,成员运算符函数的返回类型

auto operator[](const char*)
{
    return *this;
}

匿名类型,例如T,是以下发明函数模板的调用f(*this) 中参数u 的推导类型:

template <class U> void f(U u);

其中,如上所述,*this 是一个左值,因此推导出返回类型为T;即匿名类的类型。

使用相同的参数,返回成员运算符函数

auto& operator[](const char*)
{
    return *this;
}

匿名类型,比如T,是T&amp;

根据上面的论点,该类是匿名的并不重要,因为返回类型仅从 return 语句推导出来。


占位符返回类型推导:decltype(auto)(C++14 及更高版本)

如果我们将占位符返回类型auto 替换为占位符类型decltype(auto),则不同的规则将控制如何确定返回类型。

decltype(auto) operator[](const char*)
{
    return *this;
}

来自[dcl.type.auto.deduct]/5 [摘录,强调我的]:

如果占位符是 decltype(auto) 类型说明符,则 T 应该是单独的占位符。为 T 推导的类型按照 [dcl.type.simple] 中的描述确定,就好像 edecltype 的操作数

并且,从[dcl.type.simple]/4[dcl.type.simple]/4.3 适用[提取]:

对于表达式edecltype(e) 表示的类型定义为 如下:

  • [...]
  • (4.4) 否则,如果e 是左值,则decltype(e)T&amp;,其中Te 的类型;

如上所述,e(return 语句;*this)是左值,[dcl.type.simple]/4.1[dcl.type.simple]/4.2[dcl.type.simple]/4.3 均不适用于此处。

因此,使用decltype(auto)占位符类型修改的OP示例中的返回类型为T&amp;

【讨论】:

    【解决方案5】:

    它是T,其中T是类的未命名类型。

    即使没有已知名称,该类型仍然存在,并且可以通过autodecltype 等机制“使用”。

    不过,您可能想要auto&amp;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多