模板参数类型推导误区

问题描述 投票:0回答:2

我有一个带有通用(转发)参数的模板函数,如下所示:

#include <iostream>
#include <type_traits>

template <typename T>
void foo(T&& arg)
{
    // Check the type of T
    if constexpr (std::is_lvalue_reference_v<T>) {
        std::cout << "T is an lvalue reference" << std::endl;
    } else if constexpr (std::is_rvalue_reference_v<T>) {
        std::cout << "T is an rvalue reference" << std::endl;
    } else {
        std::cout << "T is neither lvalue nor rvalue reference" << std::endl;
    }
}

int main() 
{
    foo(10);
    return 0;
}

在这段代码中,我有一个函数模板 foo ,它带有一个名为 arg 的通用(转发)引用参数。该函数的目的是检查模板参数T的类型。

在主函数中,我使用参数 10 调用 foo。由于 10 是右值,所以我希望结果为“T 是右值引用”。但是,当我使用

g++ -std=c++17 -Wall -pthread -fno-elide-constructors -Qn main.cpp && ./a.out
标志在“http://coliru.stacked-crooked.com/”在线编译器中编译并运行此代码时,实际输出是
T is neither lvalue nor rvalue reference.

我的假设是模板类型

T
在这里将被推导为
int&&
。但我猜结果是
int
,这与
decltype(10)
的行为类似。这里的类型推导和调用
decltype(X)
一样吗?或者说这里面有什么规则吗?

此外:

如果按如下方式调用 foo,

int x = 10;
foo(std::move(x));

结果是

T is neither lvalue nor rvalue reference
?我期待
T is an rvalue reference

c++ c++17
2个回答
5
投票

您正在检查

T
是否是右值引用,而您应该检查
T&&
是否是右值引用。

参数类型(可以是左值引用或右值引用)拼写为

T&&

在编译器资源管理器中演示


通过此修复,您也将永远达到

std::cout << "T&& is neither lvalue nor rvalue reference" << std::endl;

不可能

T&&
不可以作为参考


4
投票

只有一个例外,模板类型参数不会被推导为引用类型,也永远不会被推导为右值引用。

唯一一个例外是通用引用起作用的原因:当函数模板接受

T&&
形式的参数时,如果左值作为该参数传递,则
T
被推导为左值引用类型。

所以在你的例子中:

  • 鉴于调用
    foo(10)
    T
    被推断为
    int
    。 这使得类型为
    arg
    int&&
  • 鉴于调用
    foo(some_int)
    T
    被推断为
    int&
    。 这意味着
    arg
    的类型将是
    int& &&
    。 由于您无法引用引用,因此将应用引用折叠规则,并且
    int& &&
    折叠为
    int&
© www.soinside.com 2019 - 2024. All rights reserved.