【问题标题】:How can a compiler deduce this class template with forwarding reference?编译器如何通过转发引用推断出此类模板?
【发布时间】:2018-09-13 01:57:57
【问题描述】:

我正在研究自 C++17 以来可用的类模板推导。这是我想问的代码:

#include <iostream>
#include <cmath>
using std::endl;
using std::cout;

template<typename T>
struct MyAbs {
     template<typename U>
     MyAbs(U&& u) : t(std::forward<T>(u))
     { cout << "template" << endl;}

#ifdef ON
    MyAbs(const T& t) : t(t) {}
#endif

    T operator()() const
    {
        return std::abs(t);
    }
    T t;
};

/*
  // may need the following
template<typename U>
MyAbs(U&&) -> MyAbs<typename std::remove_reference<U>::type>;
*/

int main()
{
    const double d = 3.14;
    cout << MyAbs(4.7)() << endl;
    cout << MyAbs(d)() << endl;    
    return 0;
}

MyAbs(const T&amp;) 没有条件编译时(即没有-DON),clang++ 和g++ 都无法推导出模板参数T。给定-DON=1,两个编译器都构建了上面的简单示例。

首先,我也猜到应该扣不成功;编译器可以推断出U,但不能推断出T。我得到的编译错误是我所期望的。如果我弄错了,请告诉我。

如果我是正确的,那么,我无法理解为什么在添加MyAbs(const T&amp;) 时使用U&amp;&amp; 的扣除成功。我期望用U&amp;&amp; 推断失败,而SFINAE 允许我在两种情况下调用MyAbs(const T&amp;):4.7 和d。然而,发生的事情是不同的。这个程序似乎调用了 4.7 的模板版本和d 的非模板版本。

$ g++ -Wall -std=c++17 ~/a.cc -DON=1
$ ./a.out 
template
4.7
3.14

模板版好像一下子变得可行了。这是预期的吗?如果有,是什么原因?

【问题讨论】:

    标签: c++ templates c++17 template-argument-deduction


    【解决方案1】:

    类模板参数推导分两个阶段进行。

    1. 推导出类模板参数。
    2. 然后,使用具体的类类型进行实际构造。

    第 1 步仅帮助您找出类模板参数。对于步骤 2 中可能使用的实际构造函数(或类模板特化),它没有做任何事情。

    构造函数的存在可能会推动演绎,但如果使用给定的构造函数进行演绎,则说明它是否用于构造。


    所以,当你有:

    template<typename T>
    struct MyAbs {
         template<typename U>
         MyAbs(U&& u);
    };
    

    类模板参数推导失败 - 您的构造函数中没有推导指南,T 是非推导上下文。编译器只是无法在MyAbs(4.7)MyAbs(d) 中找出您想要的T

    当你添加这个时:

    template<typename T>
    struct MyAbs {
         template<typename U>
         MyAbs(U&& u);
    
         MyAbs(T const&);
    };
    

    现在可以了!在这两种情况下,它都将T 推导出为double。一旦它这样做了,我们就会继续执行重载解析,就好像我们一开始就输入了MyAbs&lt;double&gt;

    在这里,MyAbs&lt;double&gt;(4.7) 恰好更喜欢转发引用构造函数(较少的 cv 限定引用),而 MyAbs&lt;double&gt;(d) 恰好更喜欢另一个(非模板优先于模板)。这没关系,也是意料之中的,仅仅因为我们使用了一个构造函数进行演绎并不意味着我们必须专门使用该构造函数进行构造。

    【讨论】:

    • 但是当不使用 U 作为单独的模板类型时,像这样:MyAbs(T&amp;&amp; t); 并且只有一个构造函数,它不会为我编译。我知道如何解决它,但我不明白为什么我需要这样做。
    • @Miguel 我不明白这个问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 2020-01-21
    相关资源
    最近更新 更多