C ++ 0x右值参考模板参数推导

时间:2010-10-27 13:49:15

标签: c++ c++11 rvalue-reference

鉴于GMan's美味的邪恶auto_cast实用函数炮制here,我一直试图弄清楚为什么当我试图{{1来自右值(在MSVC 10.0上)。

这是我正在使用的代码:

auto_cast

尽我所能,我试图遵循C ++ 0x引用折叠规则和概述here概述的模板参数推导规则,并且据我所知,上面给出的代码应该有效。

  

回想一下,在前0xC ++中,不允许引用引用:类似于A& &安培;导致编译错误。相比之下,C ++ 0x引入了以下参考折叠规则:

     
      
  • A和&安培;成为A&
  •   
  • A和&安培;&安培;成为A&
  •   
  • A&安培;&安培; &安培;成为A&
  •   
  • A&安培;&安培; &安培;&安培;成为A&&
  •   
     

第二个规则是函数模板的特殊模板参数推导规则,它通过对模板参数的rvalue引用获取参数:

template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
  public:
    template <typename R>
    friend auto_cast_wrapper<R> auto_cast(R&& pX);

    template <typename U>
    operator U() const
    {
      return static_cast<U>( std::forward<T>(mX) );
    }

  private:
    //error C2440: 'initializing': cannot convert from 'float' to 'float &&'
    auto_cast_wrapper(T&& pX) : mX(pX) { }

    T&& mX;
};

template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
  return auto_cast_wrapper<R>( std::forward<R>(pX) );
}

int main()
{
  int c = auto_cast( 5.0f );  // from an rvalue
}
     

此处适用以下规则:

     
      
  1. 当在类型A的左值上调用foo时,则T解析为A&amp;因此,通过上面的参考折叠规则,论证类型实际上变为A&amp ;.
  2.   
  3. 当在类型A的右值上调用foo时,则T解析为A,因此参数类型变为A&amp;&amp;。
  4.   

现在,当我将鼠标悬停在对template<typename T> void foo(T&&); 的调用上时,工具提示会正确显示其返回值auto_cast( 5.0f )。这意味着编译器已正确遵循规则2:

  

当在类型A的右值上调用foo时,则T解析为A.

因为我们有一个auto_cast_wrapper<float>,构造函数应该实例化一个auto_cast_wrapper<float>。但错误消息似乎暗示它实例化为按值float&&

error showing tooltip

这是完整的错误消息,再次显示T =正确浮动而T&amp;&amp;参数变为T?

float

有什么想法吗?

3 个答案:

答案 0 :(得分:4)

你忘了std ::转发T&amp;&amp; auto_cast_wrapper构造函数的参数。这打破了转发链。编译器现在发出警告,但似乎工作正常。

template <typename T>
class auto_cast_wrapper
{
  public:
    template <typename R>
    friend auto_cast_wrapper<R> auto_cast(R&& pX);

    template <typename U>
    operator U() const
    {
      return static_cast<U>( std::forward<T>(mX) );
    }

  private:
    //error C2440: 'initializing': cannot convert from 'float' to 'float &&'
    auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }

    auto_cast_wrapper(const auto_cast_wrapper&);
    auto_cast_wrapper& operator=(const auto_cast_wrapper&);

    T&& mX;
};

template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
  return auto_cast_wrapper<R>( std::forward<R>(pX) );
}

float func() {
    return 5.0f;
}

int main()
{

  int c = auto_cast( func() );  // from an rvalue
  int cvar = auto_cast( 5.0f );

  std::cout << c << "\n" << cvar << "\n";
  std::cin.get();
}

打印一对五。

答案 1 :(得分:2)

很抱歉发布未经测试的代码。 :)

DeadMG也是正确的,也应该转发参数。我相信警告是错误的,MSVC有一个错误。从电话中考虑:

auto_cast(T()); // where T is some type

T()将存在于完整表达式的末尾,这意味着auto_cast函数,auto_cast_wrapper的构造函数和用户定义的转换都引用仍然有效对象

(由于包装器除了转换或销毁之外不能做任何事情,因此它不能超过传递给auto_cast的值。)

我修复的可能是使成员只是T。但是,您将进行复制/移动,而不是直接转换原始对象。但也许随着编译器的优化,它就会消失。


不,转发不是多余的。它维护了我们自动转换的价值类别:

struct foo
{
    foo(int&) { /* lvalue */ }
    foo(int&&) { /* rvalue */ }
};

int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue

如果我没弄错,转换运算符不应该(当然不需要)标记为const

答案 2 :(得分:2)

它不编译的原因与编译原因的原因相同:

float rvalue() { return 5.0f }

float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'  

由于a本身是一个左值,因此无法绑定到b。在auto_cast_wrapper构造函数中,我们应该再次使用std::forward<T>来解决这个问题。请注意,我们可以在上面的具体示例中使用std::move(a),但这不包括也应该使用左值的通用代码。所以auto_cast_wrapper构造函数现在变为:

template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
  public:
    ...

  private:
    auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }

    T&& mX;
};

不幸的是,这似乎现在表现出未定义的行为。我收到以下警告:

  

警告C4413:'auto_cast_wrapper :: mX':引用成员被初始化为一个在构造函数退出后不会持久的临时成员

在转换运算符被触发之前,文字似乎超出了范围。虽然这可能只是MSVC 10.0的编译器错误。从GMan's answer开始,临时的生命周期应该存在,直到完整表达式结束。