“模板化右值引用”而不是通用引用作为参数?

gla*_*des 8 c++ templates overloading forwarding-reference

我有一个类对象,它采用仅限于某个概念的通用参数。现在我想将此参数(哪种类型仍然未知)转发到construct适合此引用类型的重载:根据转发的参数选择 rvalue-ref 重载或 const ref 重载。我怎样才能做到这一点?

问题是,一旦我输入双与号,“模板化右值引用”就成为通用引用,我看不到一种方法来规避它。下面的示例显示,在这两种情况下,都会选择“贪婪”通用引用重载,即使我希望第一个实例化与 const ref 一起使用。

演示

#include <concepts>
#include <cstdio>
#include <utility>

template <typename... Args>
struct function
{
    template <std::invocable<Args...> Cb>
    function(Cb&& fn) {
        construct(std::forward<Cb>(fn));
    }

    template<typename Cb>
    auto construct(const Cb&) {
        printf("Overload for const-ref called!\n");
    }

    template <typename Cb>
    auto construct(Cb&&) {
        printf("Overload for rvalue-ref called!\n");
    }
};

struct functor
{
    auto operator()()
    {
        printf("Functor called!\n");
    }
};

int main()
{
    functor foo1;

    function myfunc{foo1};
    function myfunc2{functor{}};
}
Run Code Online (Sandbox Code Playgroud)

输出:

Overload for rvalue-ref called!
Overload for rvalue-ref called!
Run Code Online (Sandbox Code Playgroud)

更新

由于上述案例可能已经过于人为,无法真正展示我正在尝试的内容,因此您可以在此处找到更详细的示例。

Bri*_*ian 3

主要答案是您应该尽量不要这样做,因为通常没有充分的理由执行这种重载决策。当您有一个具体类型时,拥有一个采用左值引用的重载和一个采用右值引用的重载是有意义的,因为只有后者才有资格从其参数中掠夺资源。但是,一个表示“我接受任何内容,但它必须过期”的函数模板通常无法对其参数即将过期的信息执行任何有用的操作,除了最终将其转发到采用具体类型的重载之外。

然而,这是可能的。一种方法是:

template <typename Cb>
auto construct(Cb&&) requires(!std::is_reference_v<Cb>) {
    printf("Overload for rvalue-ref called!\n");
}
Run Code Online (Sandbox Code Playgroud)

这利用了这样一个事实:如果转发引用的参数是左值,则模板参数将被推导为左值引用,但如果参数是右值,则模板参数将被推导为引用类型(即非引用类型)。 )。