我有一个关于在模板类的成员函数中转发引用的问题。 该标准明确指出(here),在他们的例子中:
template<class T> struct A {
template<class U>
A(T&& x, U&& y, int* p); // x is not a forwarding reference, but y is
};
x始终是rvalue-reference,因为T不是派生的,而是完全指定的类模板参数。
在我们的一个项目中,我偶然发现了我最初的编程错误,但后来发现它正在工作。所以我整理了一个小代码示例来测试意外行为:
#include <iostream>
#include <type_traits>
namespace
{
// Small helper function to output whether the given parameter is an rvalue-reference or not
template < typename TType >
void printType( TType&& value )
{
if( std::is_rvalue_reference< decltype( value ) >::value )
{
std::cout << "rvalue-ref\n";
}
else if( std::is_lvalue_reference< decltype( value ) >::value )
{
std::cout << "lvalue-ref\n";
}
else
{
std::cout << "something else\n";
}
}
template < typename TType >
struct TestStruct
{
// TType&& should be an rvalue-reference since it is a struct-template parameter and not
// deduced
static void func( TType&& value )
{
// forward should always evaluate to std::move
printType( std::forward< TType >( value ) );
}
};
template < typename TType >
void wrapper( TType&& value )
{
TestStruct< TType >::func( std::forward< TType >( value ) );
}
// TType&& is a forwarding reference as it is a deduced template type
template < typename TType >
void func( TType&& value )
{
printType( std::forward< TType >( value ) );
}
}
int main( int argc, char** argv )
{
int a = 2;
// classic rvalue-references with a temporary
func( 2 );
wrapper( 2 );
// classic lvalue-reference with a variable
func( a );
wrapper( a );
// casted rvalue-reference with std::move
func( std::move( a ) );
wrapper( std::move( a ) );
return 0;
}
主要关注的是TestStruct结构。 value
参数显然应该是rvalue-reference,无论TType
被填充但是输出(使用gcc 7.3.0和clang 4.0.1-8测试):
rvalue-ref
rvalue-ref
lvalue-ref
lvalue-ref
rvalue-ref
rvalue-ref
在第4行显示,因为我在TType
函数中派生并转发了wrapper
,它实际上成为TestStruct
的静态成员函数中的左值引用。这对我来说有些令人难以置信,因为看起来属性(无论是否派生类型)都会传播到类模板参数。
我能提出的最佳解释是,wrapper
正确地将TType&&
作为左值引用导出,并将其传递给TestStruct
。在那里,我不知何故有一个左值引用的左值引用,我甚至不知道这是可能的。但是,当我写
TestStruct< std::decay_t< TType > >::func( std::forward< TType >( value ) );
在包装函数中。
发生了什么事?