【问题标题】:Get forwarded type from the template type and not the argument从模板类型而不是参数获取转发类型
【发布时间】:2017-08-02 08:00:02
【问题描述】:

考虑以下函数:

template <class T>
constexpr /* something */ f(T&& x) {
    // do something
}

假设我想根据传递给名为myfunction 的函数的转发参数的类型来执行 sfinae。实现此目的的一种方法是:

template <class T>
constexpr auto f(T&& x) -> decltype(myfunction(std::forward<T>(x))) {
    // do something
}

除了这样做,有没有办法在模板级别做到这一点:

// This code won't compile
template <class T, class R = decltype(myfunction(std::forward<T>(x)))>
constexpr R f(T&& x) {
    // do something
}

除了我还没有访问x 的权限,所以这段代码无法编译。有没有办法仅基于T(可能使用std::declval)来实现这一点?

注意:这不是 X/Y 问题,这只是一个示例来说明这种情况发生的位置:我不知道如何在不访问变量的情况下进行 SFINAE 转发,因为对我来说std::forward 的行为是还是有点神秘。

【问题讨论】:

    标签: c++ c++14 template-meta-programming perfect-forwarding trailing-return-type


    【解决方案1】:

    是的,std::declval 确实是关键:

    template <class T, class R = decltype(myfunction(std::declval<T>()))>
    constexpr R f(T&& x) {
        // do something
    }
    

    这将是 SFINAE 输出或 R 将是选择 myfunction 的任何重载的返回类型。

    您的问题让我觉得您需要重新了解引用折叠的工作原理;我建议阅读该领域的内容(this 似乎是一个很好的起点)。

    【讨论】:

    • 我会提到这是一种糟糕的方法,因为可以通过传递第二个参数来轻松打破它。
    • @skypjack :是的,因此避免了任何 SFINAE 的发生。
    • 是的,我说的是break it。抱歉,如果不清楚。 ;-)
    【解决方案2】:

    std::forward() 与此问题无关。 constexpr 也没有。所以让我们从超级基础开始,一个只按值返回其参数的函数:

    template <class T>
    auto f(T x) -> decltype(x);
    

    当然,我们可以只使用T,但这太简单了。现在的问题是,我们如何在保持 SFINAE 的同时将其提升为模板参数(当然这里显然没有可能的替换失败,但请耐心等待):

    template <class T, class R = decltype(x)>
    R f(T x);
    

    所以这不起作用,因为还没有x(或者更糟糕的是,在名称查找发现的其他地方还有一些不相关的x)。我们可以在尾随返回类型中使用参数名称,因为它们在范围内,但我们不能在默认模板参数中将它们用作表达式 SFINAE 的一部分,因为它们尚未在范围内。

    但是因为这些是模板参数,我们不关心它们的。我们只关心他们的类型。这不是评估表达式。所以我不需要x,我需要与x 具有相同类型的东西。首先可能是:

    template <class T, class R = decltype(T{})>
    R f(T x);
    

    这可行...只要T 是默认可构造的。但是我正在编写一个模板,我不想对我不需要的类型做出假设。所以相反,我可以这样做:

    template <class T> T make(); // never defined
    
    template <class T, class R = decltype(make<T>())>
    R f(T x);
    

    现在我们有了T 类型的任意表达式,我们可以在decltype 的默认模板参数中使用它。虽然,我们仍然make() 方面受到了一些限制 - 有些类型您不能从函数中按值返回(例如数组),因此添加它会更有用参考。而且我们需要一个比make 更不容易发生冲突的名称:

    template <class T>
    add_rvalue_reference<T> declval();
    
    template <class T, class R = decltype(declval<T>())>
    R f(T x);
    

    这正是std::declval&lt;T&gt; 的重点——在未评估的上下文中为您提供T 类型的表达式。


    回到你原来的问题。对于如何从decltype(x)decltype(declval&lt;T&gt;()),我们使用相同的思维过程,但只是将其应用于不同的表达式。我们有myfunction(std::forward&lt;T&gt;(x)),而不是x。也就是说,我们使用与参数相同的类型调用myfunction

    template <class T, class R = decltype(myfunction(std::declval<T&&>()))>
    R f(T&& x);
    

    但是由于引用折叠规则,std::declval&lt;T&amp;&amp;&gt;实际上和std::declval&lt;T&gt;是同一个函数,所以我们可以这样写:

    template <class T, class R = decltype(myfunction(std::declval<T>()))>
    R f(T&& x);
    

    【讨论】:

      猜你喜欢
      • 2011-06-30
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多