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

时间:2012-08-08 01:57:06

标签: c++ function templates overloading sfinae

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

这实际上是代码的简化版本。 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_rtdouble实例化A<double>模板。我不知道幕后发生了什么,因为我想编译器应该选择更专业的operator*(A<double>&),因此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> >' 

更新

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

    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;
    }

2 个答案:

答案 0 :(得分:4)

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

  1. 名称查找:这会找到两个版本的operator*
  2. 模板参数扣除:这将应用于步骤1中找到的所有功能。
  3. 重载决议:将选择最佳匹配
  4. 访问控制:实际上可以调用最佳匹配(即不是私有成员)
  5. 虚拟:如果涉及虚拟功能,则可能需要在vtable中进行查找
  6. 首先请注意,永远不会推断出返回类型。您只需不能在返回类型上重载。正在推导operator*的模板参数,然后替换到返回类型模板中。

    那么在电话a * b;会发生什么?首先, operator* 版本都推断出了他们的参数。对于第一次重载,T2推断为A<double>,第二次重载T2解析为double。如果出现多次重载,标准说:

      

    14.7.1隐式实例化[temp.inst]第9条

         

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

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

      

    14.8.2模板参数演绎[temp.deduct]第8条

         

    如果替换导致无效的类型或表达式,请键入   扣除失败。无效的类型或表达式就是一个   如果使用替换参数编写,则格式错误。   只有在上下文中的无效类型和表达式   函数类型及其模板参数类型可以导致a   扣除失败。 [注:取代类型的评价和   表达式可以导致副作用,例如实例化   类模板特化和/或函数模板   专业化,隐式定义函数的生成等。   这种副作用不在“直接背景”中并且可能导致   该计划格式不正确。 - 结束说明]

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

    在您更新的代码rt中,返回类型是一个直接上下文,并且替换失败不是Erorr (SFINAE)适用:非推断的函数模板只是从过载中删除分辨率设置和剩下的一个被调用。

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

    A<typename rt<T,T2>::type>

答案 1 :(得分:0)

您的远期声明使用

template<typename T> class A;

但您的定义使用 struct

template <typename T>
struct A {

除此之外,我看不出任何问题......