首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使模板右值引用参数只绑定到右值引用?

如何使模板右值引用参数只绑定到右值引用?
EN

Stack Overflow用户
提问于 2011-10-23 08:42:43
回答 4查看 8.5K关注 0票数 43

我正在编写一个网络库,并大量使用移动语义来处理文件描述符的所有权。我的一个类希望接收其他类型的文件描述符包装器并获得所有权,所以它类似于

代码语言:javascript
运行
复制
struct OwnershipReceiver
{
  template <typename T>
  void receive_ownership(T&& t)
  {
     // taking file descriptor of t, and clear t
  }
};

它必须处理多个不相关的类型,所以receive_ownership必须是一个模板,而且为了安全起见,我希望它只绑定到右值引用,这样用户在传递左值时就必须显式地声明std::move。

receive_ownership(std::move(some_lvalue));

但问题是: C++模板演绎允许不需要额外的努力就能传入一个左值。有一次,我无意中将一个左值传递给了receive_ownership,然后使用了这个左值(已清除),这实际上是在自找麻烦。

所以这里有一个问题:如何让模板只绑定到右值引用?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-10-23 08:51:05

您可以将T限制为不是左值引用,从而阻止左值绑定到它:

代码语言:javascript
运行
复制
#include <type_traits>

struct OwnershipReceiver
{
  template <typename T,
            class = typename std::enable_if
            <
                !std::is_lvalue_reference<T>::value
            >::type
           >
  void receive_ownership(T&& t)
  {
     // taking file descriptor of t, and clear t
  }
};

T添加某种限制,使其只接受文件描述符包装器也可能是一个好主意。

票数 41
EN

Stack Overflow用户

发布于 2016-10-03 17:07:52

一种简单的方法是提供一个接受左值引用的已删除成员:

代码语言:javascript
运行
复制
template<typename T> void receive_ownership(T&) = delete;

对于左值参数,这将始终是一个更好的匹配。

如果你有一个带有多个参数的函数,所有这些参数都需要是右值,我们将需要几个被删除的函数。在这种情况下,我们可能更喜欢使用SFINAE来对任何左值参数隐藏函数。

一种方法是使用C++17和Concepts TS:

代码语言:javascript
运行
复制
#include <type_traits>

template<typename T>
void receive_ownership(T&& t)
    requires !std::is_lvalue_reference<T>::value
{
     // taking file descriptor of t, and clear t
}

代码语言:javascript
运行
复制
#include <type_traits>

void receive_ownership(auto&& t)
    requires std::is_rvalue_reference<decltype(t)>::value
{
     // taking file descriptor of t, and clear t
}

更进一步,您可以定义自己的新概念,如果您想要重用它,或者只是为了更清晰,这可能会很有用:

代码语言:javascript
运行
复制
#include <type_traits>

template<typename T>
concept bool rvalue = std::is_rvalue_reference<T&&>::value;


void receive_ownership(rvalue&& t)
{
     // taking file descriptor of t, and clear t
}

注意:在GCC 6.1中,您需要将-fconcepts传递给编译器,因为它是C++17的扩展,而不是它的核心部分。

为了完整起见,下面是我的简单测试:

代码语言:javascript
运行
复制
#include <utility>
int main()
{
    int a = 0;
    receive_ownership(a);       // error
    receive_ownership(std::move(a)); // okay

    const int b = 0;
    receive_ownership(b);       // error
    receive_ownership(std::move(b)); // allowed - but unwise
}
票数 14
EN

Stack Overflow用户

发布于 2011-10-23 20:13:40

我学到了一些似乎经常让人困惑的东西:使用SFINAE是可以的,但我不能使用:

代码语言:javascript
运行
复制
std::is_rvalue_reference<T>::value

它能像我想的那样工作的唯一方法是

代码语言:javascript
运行
复制
!std::is_lvalue_reference<T>::value

原因是:我需要我的函数来接收rvalue,而不是rvalue reference。有条件地使用std::is_rvalue_reference<T>::value启用的函数将不会接收右值,而是接收右值引用。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7863603

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档