如何使用模板签为类型的RVALUE和LVALUE参考获得不同的过载?

发布于 2025-02-04 18:07:11 字数 546 浏览 3 评论 0 原文

我有一个函数 foo 通过引用进行参数,我希望它在rvalue和lvalue参考上的作用不同。 (我还应该提及 foo()尊重constness;它不会更改引用的值。)我知道,如果我写信:

template <typename T> foo(T&& x);

我已经声明了a 而不是rvalue参考

template <typename T> foo(const T& x);
template <typename T> foo(T&& x);

/iSocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers“ rel =“ nofollow noreferrer” > ``给我我想要的。

那么,我的问题是:影响两种参考之间的不同行为的正确方法是什么?

I have a function foo taking a parameter by reference, and I want it to act differently on rvalue and on lvalue references. (I should also mention foo() respects constness; it doesn't change the refered-to value.) I know that if I write:

template <typename T> foo(T&& x);

I've declared a forwarding reference, rather than an rvalue reference, meaning that this way:

template <typename T> foo(const T& x);
template <typename T> foo(T&& x);

likely won't get me what I want.

So, my question is: What's the right way to affect different behavior between the two kinds of references?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

妄断弥空 2025-02-11 18:07:11

您可以有一个LVALUE参考过载和转发参考过载:

template <typename T> void foo(T& ) { ... }
template <typename T> void foo(T&& ) { ... }

对于LVALUE,首先超负荷受到青睐。对于RVALUE,仅第二个过载是可行的。


如果您想要的是一个const lvalue参考过载和一个非const rvalue参考过载,那么您只需要在转发参考案例中添加约束:

template <typename T> void foo(T const& ) { ... }
template <typename T, REQUIRES(!std::is_reference<T>::value)>
void foo(T&& ) { ... }

其中需要是您选择的方法。现在,在我们的四种情况下:

  • 非const,lvalue:只有第一个是可行的
  • const lvalue:只有第一个是可行的
  • ,不可行,rvalue:两个可行的,第二个是更好的匹配
  • const rvalue:可行,第二个是更专业的更好的匹配

You can have an lvalue reference overload and a forwarding reference overload:

template <typename T> void foo(T& ) { ... }
template <typename T> void foo(T&& ) { ... }

For lvalues, the first overload is favored. For rvalues, only the second overload is viable.


If what you want is one const lvalue reference overload and one non-const rvalue reference overload, then you just have to add a constraint to the forwarding reference case:

template <typename T> void foo(T const& ) { ... }
template <typename T, REQUIRES(!std::is_reference<T>::value)>
void foo(T&& ) { ... }

where REQUIRES is the method of your choice. Now, for our four cases:

  • non-const, lvalue: only 1st is viable
  • const lvalue: only 1st is viable
  • non-const, rvalue: both viable, 2nd is better match
  • const rvalue: both viable, 2nd is more specialized better match
昵称有卵用 2025-02-11 18:07:11

标签调度是最简单的解决方案。

namespace details {
  template <typename T>
  void foo(std::true_type is_lvalue, const T& x) {
    std::cout << x << " is an lvalue\n";
  }
  template <typename T>
  void foo(std::false_type is_lvalue, T&& x) {
    std::cout << x << " is an rvalue\n";
  }
}
template <typename T>
void foo(T&& t) {
  return details::foo(
    typename std::is_lvalue_reference<T>::type{},
    std::forward<T>(t)
  );
}

当您实际上不想支持两种版本以进行超载分辨率目的时,Sfinae是严重的过度杀伤。

Tag dispatching is the simplest solution.

namespace details {
  template <typename T>
  void foo(std::true_type is_lvalue, const T& x) {
    std::cout << x << " is an lvalue\n";
  }
  template <typename T>
  void foo(std::false_type is_lvalue, T&& x) {
    std::cout << x << " is an rvalue\n";
  }
}
template <typename T>
void foo(T&& t) {
  return details::foo(
    typename std::is_lvalue_reference<T>::type{},
    std::forward<T>(t)
  );
}

SFINAE is serious overkill when you don't actually want to support neither version being selected for overload resolution purposes.

埋葬我深情 2025-02-11 18:07:11

如果CONSEXPR C ++ 17,则可以使用C ++ 11或使用标签调度。这样的东西。

template <typename T> 
void foo(T&& x)
{
    foo(std::addressof(x), typename std::is_lvalue_reference<T>::type{});
}
void foo(void const *value, std::true_type)
{
   // do something on lvalue
}
void foo(void const *value, std::false_type)
{
   // do something on rvalue
}

您需要保存有关该类型的信息,尽管 reinterpret_cast 稍后该指针。或建议的更好:

template <typename T> 
void foo(T&& x)
{
    foo(std::forward<T>(x), typename std::is_lvalue_reference<T>::type{});
}
template <typename T>
void foo(T &&value, std::true_type)
{
   // do something on lvalue
}
template <typename T>
void foo(T &&value, std::false_type)
{
   // do something on rvalue
}

You can use tag dispatching for c++11 or if constexpr for c++17. Something like that.

template <typename T> 
void foo(T&& x)
{
    foo(std::addressof(x), typename std::is_lvalue_reference<T>::type{});
}
void foo(void const *value, std::true_type)
{
   // do something on lvalue
}
void foo(void const *value, std::false_type)
{
   // do something on rvalue
}

You need to save info about that type although to reinterpret_cast that pointer later. Or better as suggested:

template <typename T> 
void foo(T&& x)
{
    foo(std::forward<T>(x), typename std::is_lvalue_reference<T>::type{});
}
template <typename T>
void foo(T &&value, std::true_type)
{
   // do something on lvalue
}
template <typename T>
void foo(T &&value, std::false_type)
{
   // do something on rvalue
}
放手` 2025-02-11 18:07:11

自从这个问题以来,事情发生了变化,因为C ++ 17件事有些好一点:)(取决于您喜欢这种模式)。

可以在没有任何有趣的业务的情况下实现这一目标,而 std :: enable_if 但是,它只是将有趣的业务放在其他地方,但我更喜欢它:

template <typename T>
struct Fn
{
    Fn(T &&);
    Fn(T const & v);
};

两个构造函数是您的模板类型的过载。如果需要返回值,则只需将其存储在课堂上并添加转换操作员即可。编译器使用构造函数来推断类模板类型。

缺点是,您需要用牙套而不是支架运行它。

当然,链接可以与:
https://godbolt.org/z/onzury


相同的模式确实与C ++ 11一起使用,但是EHH,不太好:

template <typename T>
bool Foo(T && value)
{
    return Fn<typename std::decay<T>::type>(std::forward<T>(value));
}

https://godbolt.org/z/6yvf7w

Things have changed since this question, since C++17 things are a little better :) (depending how you like this pattern).

This can be achieved without any funny business with std::enable_if however, its just puts the funny business elsewhere, but I like it more:

template <typename T>
struct Fn
{
    Fn(T &&);
    Fn(T const & v);
};

The two constructors are the overloads of your templated type. If you need to return a value just store it in the class and add a conversion operator. The constructors are used by compiler to deduce class template type.

Downside is, you need to run it with braces instead of brackets.

And ofcourse, a link to play around with:
https://godbolt.org/z/oNzURY


Same pattern does work with C++11, but ehh, not as nicely:

template <typename T>
bool Foo(T && value)
{
    return Fn<typename std::decay<T>::type>(std::forward<T>(value));
}

https://godbolt.org/z/6yvf7W

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文