是否可以在不进行类型推导的情况下实现类似转发引用的行为?
Is it possible to achieve forwarding reference like behaviour without type deduction?
最近我了解了通用(或转发)引用。它们仅适用于模板中的类型推导或 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");
有点。你可以type-erase向前引用一个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
更好的主意。
如果移动成本低,并且您计划 stprimg 副本,则按值获取几乎与转发参考一样快。
如果移动不比复制便宜,就拿const&
。
很少见移动贵,复制贵得多。
转发引用很有用,因为在通用代码中您不知道移动是否便宜。你知道具体的类型。
在任何情况下,该类型的 live example 都会删除前向引用。
最近我了解了通用(或转发)引用。它们仅适用于模板中的类型推导或 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");
有点。你可以type-erase向前引用一个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
更好的主意。
如果移动成本低,并且您计划 stprimg 副本,则按值获取几乎与转发参考一样快。
如果移动不比复制便宜,就拿const&
。
很少见移动贵,复制贵得多。
转发引用很有用,因为在通用代码中您不知道移动是否便宜。你知道具体的类型。
在任何情况下,该类型的 live example 都会删除前向引用。