当从封闭的模板类中导出返回类型时,函数解析失败 [英] function resolution failed when return type is deduced from enclosed template class

查看:137
本文介绍了当从封闭的模板类中导出返回类型时,函数解析失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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

 模板< typename T1,typename T2> struct rt {}; 

模板<> 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>类型;
};

template< typename T>
struct A {
template< typename T2>
typename a_rt< T,T2> :: type operator *(const T2& val)const {
typename a_rt< T,T2&
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&
cout<< A< T2&被称为 endl
return ret;
}
};

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

代码无法编译,因为编译器试图实例化 $ A< double> 的$ c> a_rt 我不知道发生了什么,因为我想象编译器应该选择更专业的运算符*(A< double>&)所以 a_rt 将仅以< double,double> 作为参数进行实例化。



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



感谢一吨!

  unittest.cpp:在实例化'a_rt< double,A< >':
unittest.cpp:198:从这里实例化
unittest.cpp:174:错误:没有类型命名为'type'in'struct rt< double,A& >'

更新



编译器似乎对以下更改感到满意。有一些细微我在这里失踪。感谢一个能够让我了解编译器在这两种情况下做什么的人。

  template< typename T2> 
A< typename rt< T,T2> :: type>运算符*(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>运算符*(const A< T2& val)const {
A< typename rt< T,T2> :: type& ret;
cout<< A< T2&被称为 endl
return ret;
}


解决方案

名称查找:找到两个版本的 operator * : / code>

  • 模板参数扣除:将应用于步骤1中找到的所有函数

  • 重载解析:将选择最佳匹配

  • 访问控制:可以调用最佳匹配不是私人成员)

  • 虚拟:如果涉及虚拟功能,可能需要在vtable中查找

  • 首先注意,返回类型永远不会被推导出来。您只需不能在返回类型上重载。正在推导 operator * 的模板参数,然后替换到返回类型模板中。



    那么在调用 a * b; 会发生什么?首先, 版本的运算符* 已推导出其参数。对于第一过载, T2 被推导为 A ,并且对于第二过载 T2 解析为 double 。如果有多个重载,标准说:


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



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


    所以在生成候选函数集时,重载解析)模板被实例化,你会得到一个错误,因为 rt 没有嵌套的类型。这就是为什么不选择更专门的第二个模板:不会发生重载分辨率。您可能希望此替换失败不会是错误。但是,标准说:


    14.8.2模板参数扣除[temp.deduct]子句8 p>

    如果替换导致无效的类型或表达式,请键入
    扣除失败。无效的类型或表达式是如果使用替换参数写入将
    不成形。

    函数类型及其模板参数类型的直接上下文中,只有无效的类型和表达式可能导致
    推导失败。 [注意:对替换类型和
    表达式的求值可能导致副效应,例如
    类模板专用化和/或函数模板
    专用化的实例化,隐式定义函数的生成等等。
    这样的边效应不在直接上下文,并且可能导致
    程序不合格。 - end note]


    在您的原始代码中, typename a_rt< T,T2> / code>返回类型不是直接上下文。只有在模板实例化期间,它被评估,然后缺少在 rt 中的嵌套类型是一个错误。



    在您更新的代码中 A< typename rt< T,T2> :: type> 上下文,并且替换失败不是错误(SFINAE)适用:未推导的函数模板被简单地从重载分辨率集中移除,并且剩下的一个被调用。



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

      A< T2&称为
    > T2&称为


    I have been trying to implement a complex number class for fixed point types where the result type of the multiply operation will be a function of the input types. I need to have functions where I can do multiply complex by complex and also complex by real number.

    This essentially is a simplified version of the code. Where A is my complex type.

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

    The code fails to compile because the compiler is trying to instantiate the a_rt template with double and A<double>. I don't know what is going on under the hood since I imagine the compiler should pick the more specialized operator*(A<double>&) so a_rt will only be instantiated with <double, double> as arguments.

    Would you please explain to me why this would not work? And if this is a limitation, how should I work around this.

    Thanks a tonne!

    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

    The compiler appears to be happy with the following change. There is some subtlety I'm missing here. Appreciate someone who can walk me through what the compiler is doing in both cases.

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

    解决方案

    Resolving function calls in C++ proceeds in five phases:

    1. name lookup: this finds two versions of operator*
    2. template argument deduction: this will be applied to all functions found in step 1)
    3. overload resolution: the best match will be selected
    4. access control: can the best match in fact be invoked (i.e. is it not a private member)
    5. virtuality: if virtual function are involved, a lookup in the vtable might be required

    First note that the return type is never ever being deduced. You simply cannot overload on return type. The template arguments to operator* are being deduced and then substituted into the return type template.

    So what happens at the call a * b;? First, both versions of operator* have their arguments deduced. For the first overload, T2 is deduced to being A<double>, and for the second overload T2 resolves to double. If there multiple overloads, the Standard says:

    14.7.1 Implicit instantiation [temp.inst] clause 9

    If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated (14.8.3).

    So at the end of argument deduction when the set of candidate functions are being generated, (so before overload resolution) the template gets instantiated and you get an error because rt does not have a nested type. This is why the more specialized second template will not be selected: overload resolution does not take place. You might have expected that this substitution failure would not be an error. HOwever, the Standard says:

    14.8.2 Template argument deduction [temp.deduct] clause 8

    If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note ]

    In your original code, the typename a_rt<T,T2>::type return type is not an immediate context. Only during template instantiation does it get evaluated, and then the lack of the nested type in rt is an erorr.

    In your updated code A<typename rt<T,T2>::type> return type is an immediate context and the Substitution Failure is Not An Erorr (SFINAE) applies: the non-deduced function template is simply removed from the overload resolution set and the remaining one is being called.

    With your updated code, output will be:

    > A<T2>& called     
    > T2& called
    

    这篇关于当从封闭的模板类中导出返回类型时,函数解析失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆