使用类模板参数转发引用

时间:2018-02-15 09:16:23

标签: c++ templates

我有一个关于在模板类的成员函数中转发引用的问题。 该标准明确指出(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 ) );

在包装函数中。

发生了什么事?

0 个答案:

没有答案