模板特化和右值引用,C++

fja*_*sze 6 c++ templates rvalue-reference template-specialization

我对这个小例子有点困惑:

using mytype = std::vector<std::string>;

template<typename T>
void test(T item)
{
    throw std::runtime_error(typeid(item).name());
}
template<>
void test(std::vector<std::string>&& vec)
{
    std::cout<<"Ok."<<std::endl;
}

int main()
{
    mytype stuff;
    test(std::forward<mytype>(stuff));
}
Run Code Online (Sandbox Code Playgroud)

我希望在这里为调用选择专门的模板,但事实并非如此,删除&&将使这种情况发生(并且参数移至vec)..

为什么test没有使用专门用于右值参数的版本?

use*_*570 2

为什么没有使用专门用于右值参数的测试版本?

std::forward<mytype>(stuff)这是因为您传递的函数参数是一个表达式,而C++ 中的表达式绝不是某种引用类型。也就是说,函数调用参数的类型std::forward<mytype>(stuff)实际上是std::vector<std::string>而不是std::vector<std::string>&&。换句话说,T将被推导出为std::vector<std::string>和 not std::vector<std::string>&&

基本上,您已经为模板参数专门化了函数模板std::vector<std::string>&&,但T被推导为std::vector<std::string>. 因此,无法使用专业化。另一方面,如果您要删除,&&那么将调用专业化(请参阅答案末尾的解释)。

让我们看一个人为的例子来澄清这个问题:

实施例1

我添加以下示例来表明我的上述解释是正确的。

template <class T> void f(T)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

void g() { 
    f((const int&)0);  //T will be deduced as int and not "const int" or even "const int&"
    f((int&&)0);       //T will be deduced as int and not "int&&"
}
int main()
{
    g();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

工作演示

实施例2

template<typename T>
void test(T item)        //#1
{
    std::cout<<"generic"<<std::endl;
}
template<>
void test(int&& vec)    //#2
{
    std::cout<<"Ok."<<std::endl;
}

int main()
{
    int stuff = 0;
    //---vvvvvvvvvvvvvvvvvvvvvvvv----------->the argument is an expression and is of type int instead of int&&
    test(std::forward<int>(stuff));         //calls #1
   
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,表达式std::forward<int>(stuff)的类型为intand not int&&,因此T被推导为int(and not int&&)。这意味着将调用通用版本。


删除 && 将会实现这一点

当您删除 时&&,那么这一次您将明确专门化函数模板 forstd::vector<std::string>和 not std::vector<std::string>&&。这意味着这一次,推导的T结果与您为其专门化函数模板的模板参数匹配,因此调用了专门化。