【问题标题】:Is it possible to achieve forwarding reference like behaviour without type deduction?是否可以在不进行类型推断的情况下实现类似转发引用的行为?
【发布时间】:2017-06-01 13:24:08
【问题描述】:

最近我了解了通用(或转发)引用。它们仅适用于模板中的类型推导或auto / decltype

当我使用左值引用 (&) 时,我无法将它绑定到右值(临时)对象。当我使用右值引用 (&&) 时,我无法将它绑定到左值对象。转发引用可以与非常量左值和右值一起使用。

是否可以实现不进行类型推导的转发引用行为?

void Foo(string && s); // rvalue-reference
void Bar(const string & s); // const lvalue-reference
void Baz(auto && s); // forwarding reference

string s = "lvalue string";

Foo(s);      // error: cannot bind lvalue to &&
Foo("test"); // OK

// works, but can't take non-const value
Bar(s);
Bar("test");

// works
Baz(s);
Baz("test");

【问题讨论】:

  • const & 是在&& 存在之前完成的。
  • 您始终可以使用 SFINAE 限制类型。
  • When I use lvalue-reference (&), I can't bind it to non-const object. 当然,您可以将左值引用绑定到非常量对象。也许您的意思是非常量引用不能绑定到 const 对象?或者那个非常量引用不能绑定到一个右值(比如一个临时值)?
  • @Rakete1111 谢谢,不知道这件事
  • 问题的真正症结在于如何处理论点(?)抛开语言律师的东西,我认为如果你把问题反过来看,生活会变得更简单您将在函数中处理该参数。如果您需要复制它,请始终按值接受然后移动,否则通过 const 引用获取它。那么你发送给它的内容并不重要 - 坦率地说 - 无关紧要。

标签: c++ c++11 language-lawyer rvalue-reference perfect-forwarding


【解决方案1】:

有点。您可以对T 的前向引用概念进行类型擦除。

template<class T>
struct forward_ref {
  using extract_sig = T(void*);
  using get_sig = T const&(void*);

  extract_sig* extract = nullptr;
  get_sig* gettor = nullptr;

  void* pvoid;


  template<class U>
  void wrap( U&& t ) {
    auto* pt = std::addressof(t);
    using pT = decltype(pt);
    pvoid = const_cast<void*>(static_cast<void const*>(pt));
    gettor = [](void* pvoid)->T const& {
      // std::cout << "getting:\n";
      auto* pt = static_cast<pT>(pvoid);
      return *pt;
    };
    extract = [](void* pvoid)->T {
      // std::cout << "extracting via " << (std::is_rvalue_reference<U&&>::value?"move":"copy") << "\n";
      auto* pt = static_cast<pT>(pvoid);
      return std::forward<U>(*pt);
    };
  }

  forward_ref(T const& t){ wrap(t); }
  forward_ref(T && t = {}){ wrap(std::move(t)); }


  bool valid() const { return gettor&&extract; }
  operator T()&&{
    // std::cout << "operator T&&\n";
    T r = extract(pvoid);
    // std::cout << "extracted\n";
    gettor=nullptr; extract=nullptr; pvoid=nullptr;
    return r;
  }
  operator T const&() const &{
    // std::cout << "operator T const&\n";
    return gettor(pvoid);
  }
  T get()&& { return std::move(*this); }
  T const& get() const& { return *this; }
};

所以你可以这样做:

void Foo(forward_ref<std::string> s); // rvalue-reference

s 的行为(在某种程度上)类似于对std::string 的转发引用。

如果您从s 复制std::move,它要么复制传入的std::string,要么根据它是右值还是左值移动它。

现在,这很愚蠢,因为

void Foo(std::string s)

这几乎是一个比上面那个花哨的forward_ref 更好的主意。

如果移动很便宜,并且您计划复制一个副本,那么按价值获取几乎与转发参考一样快。

如果搬家不比复制便宜,那就去const&amp;吧。

搬家贵,复制贵得多。

转发引用很有用,因为在通用代码中您不知道 move 是否便宜。用你知道的具体类型。

无论如何,该类型的live example 已擦除前向引用。

【讨论】:

    猜你喜欢
    • 2023-04-01
    • 1970-01-01
    • 2018-07-18
    • 2012-01-16
    • 1970-01-01
    • 2020-10-11
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多