这是通用参考吗?std :: forward在这里有意义吗? [英] Is this a universal reference? Does std::forward make sense here?

查看:59
本文介绍了这是通用参考吗?std :: forward在这里有意义吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码片段,它使用使函数模板构造专门针对推导类型的类模板的实例的常见用法,如 std :: make_unique 所示.std :: make_tuple ,例如:

 模板< typename T>结构foo{std :: decay_t< T>v_;foo(T& v):v_(std :: forward< T>(v)){}};模板< typename U>foo< U>make_foo(U& v){返回{std :: forward< U>(v)};} 

在斯科特·迈耶斯(Scott Meyers)的通用参考文献"中, make_foo 是通用参考,因为其类型为 U&& ,其中 U 为推论. foo 的构造函数的参数不是通用引用因为尽管其类型为 T&& ,但通常不会推导 T .

但是,在 foo 的构造函数被 make_foo 调用的情况下,在我看来,考虑构造函数的参数可能很有意义之所以将 foo 作为通用参考,是因为 T 是由功能模板 make_foo .相同的参考折叠规则将适用因此, v 的类型在两个函数中都相同.在这种情况下,两个 T 可以说是 U .

所以我的问题是双重的:

  • foo 的构造函数的参数认为是合理的在推导 T 的有限情况下的通用参考在调用者的通用引用上下文中,例如我的示例?
  • 在我的示例中,使用 std :: forward 明智吗?

解决方案

make_foo 与"right"在同一个球场,但 foo 不是.目前,仅 foo 构造函数接受非推断的 T& ,并且转发那里可能不是您的意思(但请参阅@nosid的评论).总而言之, foo 应该带有类型参数,具有模板化的构造函数,并且maker函数应该执行衰减:

 模板< typename T>结构foo{电视_;模板< typename U>foo(U& u):v_(std :: forward< U>(u)){}};模板< typename U>foo< typename std :: decay< U> :: type>make_foo(U& u){返回foo< typename std :: decay< U> :: type>(std :: forward< U>(u));} 

在C ++ 14中,maker函数的编写变得更加简单:

 模板< typename U>自动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指出了这一点),但这绝对不是转发".)/p>

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

Consider this snippet of code, which uses the common idiom of having a function template construct an instance of a class template specialized on a deduced type, as seen with std::make_unique and std::make_tuple, for example:

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) };
}

In the context of Scott Meyers' "universal references", the argument to make_foo is a universal reference because its type is U&& where U is deduced. The argument to the constructor of foo is not a universal reference because although its type is T&&, T is (in general) not deduced.

But in the case in which the constructor of foo is called by make_foo, it seems to me that it might make sense to think of the argument to the constructor of foo as being a universal reference, because T has been deduced by the function template make_foo. The same reference collapsing rules will apply so that the type of v is the same in both functions. In this case, both T and U can be said to have been deduced.

So my question is twofold:

  • Does it make sense to think of the argument to the constructor of foo as being a universal reference in the limited cases in which T has been deduced within a universal reference context by the caller, as in my example?
  • In my example, are both uses of std::forward sensible?

解决方案

make_foo is in the same ballpark as "right", but foo isn't. The foo constructor currently only accepts a non-deduced T &&, and forwarding there is probably not what you mean (but see @nosid's comment). All in all, foo should take a type parameter, have a templated constructor, and the maker function should do the decaying:

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));
}

In C++14 the maker function becomes a bit simpler to write:

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


As your code is written now, int a; make_foo(a); would create an object of type foo<int &>. This would internally store an int, but its constructor would only accept an int & argument. By contrast, make_foo(std::move(a)) would create a foo<int>.

So the way you wrote it, the class template argument determines the signature of the constructor. (The std::forward<T>(v) still makes sense in a perverted kind of way (thanks to @nodis for pointing this out), but this is definitely not "forwarding".)

That is very unusual. Typically, the class template should determine the relevant wrapped type, and the constructor should accept anything that can be used to create the wrapped type, i.e. the constructor should be a function template.

这篇关于这是通用参考吗?std :: forward在这里有意义吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆