【问题标题】:C++0x perfect forwarding getting in the way of copy ctor?C++0x 完美转发妨碍了复制 ctor?
【发布时间】:2011-06-06 08:30:59
【问题描述】:

使用 MSVC2010...

我有一个包装 std::string 的结构,定义了标准移动 ctor,以及一个完美的转发 ctor,用于将参数转发到 std::string ctor。

struct Wrapper
{
  std::string value;

  Wrapper()
  {
  }

  Wrapper( Wrapper const& rhs )
    :value(rhs.value)
  {
  }

  Wrapper( Wrapper&& rhs )
    :value(std::move(rhs.value))
  {
  }

  Wrapper& operator=( Wrapper const& rhs )
  {
    value = rhs.value;
    return *this;
  }

  Wrapper& operator=( Wrapper&& rhs )
  {
    value = std::move(rhs.value);
    return *this;
  }


  template<typename StringT>
  Wrapper( StringT&& value )
    :value(std::forward<StringT>(value))
  {
  }
};

但现在看来我无法从另一个包装器复制构造一个包装器

Wrapper w0;
Wrapper w1(w0);

这会导致指向完美转发 ctor 的编译错误,说它无法将 Wrapper 转换为 std::string。这是正确的行为吗?编译器不应该调用复制ctor,而不是模板化重载吗?

1>t:\depot\warp\code\apps\pf_test\main.cpp(56): error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax> &'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]
1>          Reason: cannot convert from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          t:\depot\warp\code\apps\pf_test\main.cpp(63) : see reference to function template instantiation 'Wrapper::Wrapper<Wrapper&>(StringT)' being compiled
1>          with
1>          [
1>              StringT=Wrapper &
1>          ]
1>

如果我定义另一个复制 ctor,一个对 Wrapper 进行非常量引用的复制 ctor(如下所示),那么一切似乎都很好......这是前进的方向吗?还是我搞砸了什么?或者这是 VS2010 的 bug?

  Wrapper( Wrapper& rhs )
    :value(rhs.value)
  {
  }

【问题讨论】:

  • 您知道,您不需要手写所有这些构造函数。他们增加了使用 'Wrapper() = default;' 的能力句法。它会为您提供默认的构造函数和赋值运算符(您需要为所需的每个 ctr/op 执行此操作)。
  • 感谢您的提示 - VC2010 似乎还不支持此功能
  • 对不起,我有时会忘记,因为我主要使用 gcc。

标签: c++11


【解决方案1】:

是的,这是正确的行为。

在这种情况下可能会调用两个构造函数 - 您的复制构造函数和模板一个。但是,模板构造函数可能会推断出类型为完美匹配Wrapper(StringT&amp;&amp;)StringT = Wrapper&amp; 产生完美匹配Wrapper(Wrapper&amp;)),因此被使用而不是复制构造函数。

建议的解决方法 - 使用来自 &lt;type_traits&gt;std::enable_if

template <typename StringT>
Wrapper(StringT&& value,
  typename std::enable_if<
    !std::is_same<
      StringT,
      Wrapper&
    >::value
  >::type* = 0)
  : value(std::forward<StringT>(value))
{ }

看到它工作here

【讨论】:

  • 有趣...所以在编译器甚至认为我选择的左值不能绑定到右值引用 StringT&& 之前就选择了模板重载?我不确定我是否购买它,因为错误消息位于字符串 ctor 的位置,似乎已经确定参数类型是兼容的?还是我只是对这一切得到解决的顺序有误解
  • 左值可以绑定StringT&&。由于引用折叠规则,StringT&amp;&amp;StringT = Wrapper&amp;(即 Wrapper&amp; &amp;&amp;)折叠成 Wrapper&amp;
  • @MikeNicolella 模板构造函数会吃掉您尝试构造它的非常量引用 (Wrapper&)。这就是为什么当你添加 Wrapper(Wrapper& w) 它应该可以工作的原因。如果您使用一个接受多个输入(2 个或多个值而不是一个值)的类进行测试,您不会注意到这个问题。
  • 啊哈,我错过了 T&& 实际上可以绑定到左值。哼。谢谢!
  • @MikeNicolella:T&& 不能绑定到左值。这里的技巧在于完美转发到左值的 T- 的细节,它变成 (const) T&,其中 T 是基础值类型,例如诠释。因此,实际引用类型最终是(可能是 const)T&,因为 ((const)T&)&& 衰减为 (const) T&。
猜你喜欢
  • 2014-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-11
  • 2022-01-22
  • 2015-05-08
  • 1970-01-01
相关资源
最近更新 更多