【问题标题】:Unexpected return type when combining std::forward, std::move and volatile组合 std::forward、std::move 和 volatile 时出现意外的返回类型
【发布时间】:2016-04-27 14:28:51
【问题描述】:

代码on gcc.godbolt.org

我创建了一个简单的类型特征来删除右值引用:

template <typename T>
struct remove_rvalue_reference { using type = T; };

template <typename T>
struct remove_rvalue_reference<T&&> { using type = T; };

template <typename T>
using remove_rvalue_reference_t = 
    typename remove_rvalue_reference<T>::type;

我用它来实现一个copy_if_rvalue(x) 函数,它的返回类型取决于传递的参数:

template <typename T>
constexpr auto copy_if_rvalue(T && x) 
  -> remove_rvalue_reference_t<decltype(std::forward<decltype(x)>(x))>
{
    return std::forward<decltype(x)>(x);
}

为了确保函数返回正确的类型,我编写了一些简单的静态断言:

// literal
static_assert(std::is_same<
    decltype(copy_if_rvalue(0)), int
>{});

// lvalue
int lv = 10;
static_assert(std::is_same<
    decltype(copy_if_rvalue(lv)), int&
>{});

// const lvalue
const int clv = 10;
static_assert(std::is_same<
    decltype(copy_if_rvalue(clv)), const int&
>{});

// rvalue
int rv = 10;
static_assert(std::is_same<
    decltype(copy_if_rvalue(std::move(rv))), int
>{});

// volatile lvalue
volatile int vlv = 10;
static_assert(std::is_same<
    decltype(copy_if_rvalue(vlv)), volatile int&
>{});

// const lvalue
volatile const int vclv = 10;
static_assert(std::is_same<
    decltype(copy_if_rvalue(vclv)), volatile const int&

以上所有静态断言都编译成功。但是,当尝试 std::move volatile int 变量时,会发生意外情况:

// volatile rvalue
volatile int vrv = 10;

// (0) fails:
static_assert(std::is_same<
    decltype(copy_if_rvalue(std::move(vrv))), volatile int
>{});

// (1) unexpectedly passes:
static_assert(std::is_same<
    decltype(copy_if_rvalue(std::move(vrv))), int
>{});

// (2) unexpectedly passes:
static_assert(std::is_same<
    remove_rvalue_reference_t<decltype(std::forward<decltype(vrv)>(std::move(vrv)))>, 
    volatile int
>{});

断言 (0) 失败 - volatile 不会传播,如断言 (1) 所示。

但是,断言(2)通过了,即使我认为它应该等同于断言(0),因为copy_if_rvalue的返回类型与(2)的第一种类型完全相同:

// (from assertion (2))
remove_rvalue_reference_t<decltype(std::forward<decltype(vrv)>(std::move(vrv)))> 

// ...should be equivalent to...

// (from copy_if_rvalue)
-> remove_rvalue_reference_t<decltype(std::forward<decltype(x)>(x))>

似乎volatile 仅在使用std::move 时才传播,并且仅通过copy_if_rvalue 模板函数传播。

这是怎么回事?

【问题讨论】:

标签: c++ language-lawyer c++14 decltype trailing-return-type


【解决方案1】:

没有 cv 限定的标量纯右值。 [expr]/6:

如果纯右值最初的类型为“cv T”,其中T 是 cv-unqualified 非类,非数组类型,表达式的类型 在任何进一步分析之前调整为T

即同样的规则给出

int const f();
f() // <=

type int 也适用于此。如果您尝试使用某些类类型而不是 int(例如 std::string),您将获得预期的类型。

【讨论】:

  • 谢谢。这解释了意外的断言结果。这条规则的基本原理是什么(具体来说,为什么这只适用于标量类型)
  • @VittorioRomeo 好吧,(至少在 C++03 中)const int 的纯右值的行为与 int 类型的纯右值一样,将其视为一个可能会简化其他地方的分析。当然,类类型有区别(想想 const 成员函数),数组也有区别(参见core issue 1059)。我引用的措辞是由core issue 1261 添加的。另外,请查看core issue 1026(其中涵盖了“为什么”)。
猜你喜欢
  • 2016-07-08
  • 1970-01-01
  • 2015-05-03
  • 2016-03-21
  • 2017-10-19
  • 1970-01-01
  • 2018-07-05
  • 1970-01-01
相关资源
最近更新 更多