decltype vs auto

decltype vs auto

本文关键字:auto vs decltype      更新时间:2023-10-16

据我所知,decltypeauto都会试图弄清楚某种东西的类型

如果我们定义:

int foo () {
    return 34;
}

那么这两种声明都是合法的:

auto x = foo();
cout << x << endl;
decltype(foo()) y = 13;
decltype(auto)  y = 13; // alternatively, since C++14
cout << y << endl;

你能告诉我decltypeauto的主要区别吗?

decltype给出传递给它的表达式的声明的类型。auto执行与模板类型推导相同的操作。因此,例如,如果您有一个返回引用的函数,auto仍然是一个值(您需要auto&才能获得引用),但decltype将恰好是返回值的类型。

#include <iostream>
int global{};
int& foo()
{
   return global;
}
 
int main()
{
    decltype(foo()) a = foo(); //a is an `int&`
 // decltype(auto)  a = foo();   alternatively, since C++14
    auto b = foo(); //b is an `int`
    b = 2;
    
    std::cout << "a: " << a << 'n'; //prints "a: 0"
    std::cout << "b: " << b << 'n'; //prints "b: 2"
    std::cout << "---n";
    decltype(foo()) c = foo(); //c is an `int&`
 // decltype(auto)  c = foo();   alternatively, since C++14
    c = 10;
    
    std::cout << "a: " << a << 'n'; //prints "a: 10"
    std::cout << "b: " << b << 'n'; //prints "b: 2"
    std::cout << "c: " << c << 'n'; //prints "c: 10"
 }

另请参阅David Rodríguez关于autodecltype中只有一个可能的地方的回答。

这是C++11对C++11问题的回答

auto(在推断类型的上下文中)仅限于定义有初始值设定项的变量的类型。decltype是一个更广泛的构造,以额外信息为代价,可以推断表达式的类型。

在可以使用auto的情况下,它比decltype更简洁,因为您不需要提供从中推断类型的表达式。

auto x = foo();                           // more concise than `decltype(foo()) x`
std::vector<decltype(foo())> v{ foo() };  // cannot use `auto`

当函数使用尾随返回类型时,关键字auto也用于完全无关的上下文:

auto foo() -> int;

auto只是一个前导,因此编译器知道这是一个带有尾部返回类型的声明。虽然上面的例子可以简单地转换为旧样式,但在通用编程中它是有用的:

template <typename T, typename U>
auto sum( T t, U u ) -> decltype(t+u)

请注意,在这种情况下,auto不能用于定义返回类型。

这是我对auto和decltype:的思考

两者在实践上最明显的区别是:

  • 在expr的类型推导中,decltype推导出正确的类型(除了左值expr->左值ref),auto默认为值

我们需要学习"数据流"模型,然后才能理解差异。

在我们的代码中,函数调用可以解析为数据流模型(类似于函数程序的概念),因此被调用的函数是数据接收器,调用方是<strong]数据提供者>。很明显,数据类型必须由数据接收器决定,否则数据无法在数据流中按顺序组织。

看这个:

template<typename T>
void foo(T t){
    // do something.
}

无论您是否通过,T都将被推导为值类型。如果你想要ref类型,你应该使用auto&auto&&,这就是我要说的,数据类型由数据接收器决定。

让我们回到auto:

auto用于对右值表达式进行类型推导,为数据接收器提供接收数据的正确接口。

auto a = some expr; // a is data receiver, and the expr is the provider.

那么为什么auto会忽略ref修饰符呢?因为它应该由接收器决定。

为什么我们需要decltype

答案是:auto不能用作真正的类型推导,它不会给你正确的表达式类型。它只是给数据接收器一个正确的类型来接收数据。

因此,我们需要decltype来获得正确的类型。

通常,如果需要初始化变量的类型,请使用autodecltype在需要非变量的类型(如返回类型)时更适合使用。

修改@Mankarse的示例代码,我认为一个更好的代码失败了:

#include <iostream>
int global = 0;
int& foo()
{
   return global;
}
int main()
{
    decltype(foo()) a = foo(); //a is an `int&`
    auto b = foo(); //b is an `int`
    b = 2;
    std::cout << "a: " << a << 'n'; //prints "a: 0"
    std::cout << "b: " << b << 'n'; //prints "b: 2"
    std::cout << "global: " << global << 'n'; //prints "global: 0"
    std::cout << "---n";
    //a is an `int&`
    a = 10;
    std::cout << "a: " << a << 'n'; //prints "a: 10"
    std::cout << "b: " << b << 'n'; //prints "b: 2"
    std::cout << "global: " << global << 'n'; //prints "global: 10"
    return 0;
}

我认为auto是一个纯粹的简化功能,而decltype的主要目的是在基础库中实现复杂的元编程。然而,他们非常亲密从语言技术的使用角度来看是相关的。

来自HOPL20 4.2.1,Bjarne Stroustrup。