从封闭的模板类推导返回类型时,函数解析失败

2024-03-20

我一直在尝试为定点类型实现一个复数类,其中乘法运算的结果类型将是输入类型的函数。我需要有一些函数,可以将复数乘以复数,也可以将复数乘以实数。

这本质上是代码的简化版本。其中 A 是我的复杂类型。

template<typename T1, typename T2> struct rt {};

template<> struct rt<double, double> { 
    typedef double type;
};
//forward declaration
template<typename T> struct A;

template<typename T1, typename T2>
struct a_rt {
    typedef A<typename rt<T1,T2>::type> type;
};

template <typename T>
struct A {
    template<typename T2>
    typename a_rt<T,T2>::type operator*(const T2& val) const {
        typename a_rt<T,T2>::type ret;
        cout << "T2& called" << endl;
        return ret;
    }
    template<typename T2>
    typename a_rt<T,T2>::type operator*(const A<T2>& val) const {
        typename a_rt<T,T2>::type ret;
        cout << "A<T2>& called" << endl;
        return ret;
    }
};

TEST(TmplClassFnOverload, Test) {
    A<double> a;
    A<double> b;
    double c;
    a * b;
    a * c;
}

代码无法编译,因为编译器正在尝试实例化a_rt模板与double and A<double>。我不知道幕后发生了什么,因为我想编译器应该选择更专业的operator*(A<double>&) so a_rt只会被实例化<double, double>作为参数。

您能向我解释一下为什么这行不通吗? 如果这是一个限制,我应该如何解决这个问题。

万分感谢!

unittest.cpp: In instantiation of 'a_rt<double, A<double> >':
unittest.cpp:198:   instantiated from here 
unittest.cpp:174: error: no type named 'type' in 'struct rt<double, A<double> >' 

Update

编译器似乎对以下更改感到满意。我在这里缺少一些微妙之处。感谢能够引导我了解编译器在这两种情况下所做的事情的人。

    template<typename T2>
    A<typename rt<T,T2>::type> operator*(const T2& val) const {
        A<typename rt<T,T2>::type> ret;
        cout << "T2& called" << endl;
        return ret;
    }
    template<typename T2>
    A<typename rt<T,T2>::type> operator*(const A<T2>& val) const {
        A<typename rt<T,T2>::type> ret;
        cout << "A<T2>& called" << endl;
        return ret;
    }

解析 C++ 中的函数调用分为五个阶段:

  1. 姓名查询:这找到了两个版本operator*
  2. 模板实参推导:这将应用于步骤 1 中找到的所有函数)
  3. 过载解析:将选择最佳匹配
  4. 访问控制:实际上可以调用最佳匹配吗(即它不是私有成员)
  5. 虚拟性:如果涉及虚函数,可能需要在vtable中查找

首先请注意,返回类型永远不会被推导。你只需不能重载返回类型。模板参数operator*正在被推导,然后取代的到返回类型模板中。

那么通话时会发生什么a * b;?第一的,both的版本operator*推导出他们的论点。对于第一次过载,T2被推论为A<double>,对于第二次过载T2决心double。如果存在多个过载,标准规定:

14.7.1 隐式实例化 [temp.inst] 子句 9

如果函数模板或成员函数模板特化是 以涉及重载决议的方式使用,声明 专业化是隐式实例化的(14.8.3)。

因此,在参数推导结束时,当生成候选函数集时(因此在重载解析之前),模板将被实例化,并且您会收到错误,因为rt没有嵌套type。这就是为什么不会选择更专业的第二个模板的原因:不会发生重载解析。您可能预计此替换失败不会是一个错误。然而,标准说:

14.8.2 模板参数推导 [temp.deduct] 子句 8

如果替换导致无效类型或表达式,请键入 扣除失败。无效的类型或表达式是 如果使用替换的参数编写,则格式不正确。 仅在直接上下文中无效的类型和表达式 函数类型及其模板参数类型可以导致 扣除失败。 [注:替代类型的评估和 表达式可能会导致副作用,例如实例化 类模板专业化和/或函数模板 专业化、隐式定义函数的生成等。 这种副作用并不存在于“直接环境”中,并且可能导致 该程序格式不正确。 ——尾注]

在你的原始代码中,typename a_rt<T,T2>::type返回类型不是直接上下文。仅在模板实例化期间才会对其进行求值,然后缺少嵌套type in rt是一个错误。

在您更新的代码中A<typename rt<T,T2>::type>返回类型是直接上下文并且替换失败不是错误(SFINAE) 适用:将非推导函数模板简单地从重载决策集中删除,并调用剩余的函数模板。

使用更新后的代码,输出将是:

> A<T2>& called     
> T2& called
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从封闭的模板类推导返回类型时,函数解析失败 的相关文章

随机推荐