这是一个普遍的参考? std :: forward在这里有意义吗?

时间:2014-06-30 19:15:40

标签: c++ c++11 language-lawyer perfect-forwarding universal-reference

考虑这段代码,它使用具有函数模板的常用习惯用法构建专门针对推导类型的类模板的实例,如std::make_uniquestd::make_tuple所示,例如:

template <typename T>
struct foo
{
    std::decay_t<T> v_;
    foo(T&& v) : v_(std::forward<T>(v)) {}
};

template <typename U>
foo<U> make_foo(U&& v)
{
    return { std::forward<U>(v) };
}

在斯科特迈耶斯的“普遍引用”的背景下,论证 make_foo是一种通用引用,因为其类型为U&&,其中Ufoo 推导。 T&&的构造函数的参数不是通用引用 因为虽然它的类型是T,但foo(通常)不会推断出来。

但是在make_foo调用foo的构造函数的情况下,它就是这样 在我看来,想到构造函数的参数可能是有意义的 T作为一个通用引用,因为make_foo已被推导出来 功能模板v。相同的参考折叠规则将适用 这样两个函数中T的类型相同。在这种情况下,U 可以说foo已被推断出来。

所以我的问题有两个:

  • T的构造函数的参数视为存在是否有意义 在推导std::forward的有限情况下的通用参考 在调用者的通用引用上下文中,如我的例子中所示?
  • 在我的例子中,{{1}}是否合理使用?

2 个答案:

答案 0 :(得分:15)

make_foo和“正确”在同一个球场,但foo不是。 foo构造函数当前接受非推导的T &&,转发可能不是您的意思(但请参阅@nosid的评论)。总而言之,foo应该采用类型参数,具有模板化构造函数,并且制造商函数应该进行衰减:

template <typename T>
struct foo
{
    T v_;

    template <typename U>
    foo(U && u) : v_(std::forward<U>(u)) { }
};

template <typename U>
foo<typename std::decay<U>::type> make_foo(U && u)
{
    return foo<typename std::decay<U>::type>(std::forward<U>(u));
}

在C ++ 14中,maker函数变得有点简单:

template <typename U>
auto make_foo(U && u)
{ return foo<std::decay_t<U>>(std::forward<U>(u)); }

现在编写代码时,int a; make_foo(a);会创建foo<int &>类型的对象。这将在内部存储int,但其构造函数只接受int &参数。相比之下,make_foo(std::move(a))会创建foo<int>

所以你编写它的方式,类模板参数决定了构造函数的签名。 (std::forward<T>(v)仍然以一种变态的方式有意义(感谢@nodis指出这一点),但这绝对不是“转发”。)

这很不寻常。通常,类模板应确定相关的包装类型,构造函数应接受可用于创建包装类型的任何内容,即构造函数应为函数模板。

答案 1 :(得分:2)

没有#34;通用参考&#34;的正式定义,但我将其定义为:

  

通用引用是类型为[template-parameter] &&的函数模板的参数,其目的是可以从函数参数推导出模板参数,并且参数将通过左值引用或适当的右值引用传递。

因此,根据该定义,不,T&& v的构造函数中的foo参数不是通用引用。

然而,短语的全部内容&#34;通用参考&#34;是为我们人类在设计,阅读和理解代码时提供思考的模型或模式。说{&#34;当make_foo调用foo<U>的构造函数时,模板参数T已从make_foo的参数推断出来是合理且有用的。一种允许构造函数参数T&& v适当地为左值引用或右值引用的方法。&#34;这与我可以很好地转向声明的概念非常接近:&#34;当make_foo调用foo<U>的构造函数时,构造函数参数T&& v本质上是普遍参考。&#34;

是的,std::forward的两种用法都会按照您的意图执行,允许成员v_在可能的情况下从make_foo参数移出或以其他方式复制。但让make_foo(my_str)返回foo<std::string&>,而不是foo<std::string>,其中包含my_str的副本,这是非常令人惊讶的....