我有以下代码无法编译,抱怨+ =运算符不存在。 + =运算符在此处声明为A类之外。
template < typename _T >
class A {
public:
operator _T () const { return 42 ; }
};
template <typename _T >
A< _T > & operator += ( A< _T > & l, _T r ) { return l ; }
int main() {
A< int > e, f ;
e += f ;
return 0 ;
}
但是,如果我在A类中实现运算符,代码编译并运行:
template < typename _T >
class A {
public:
operator _T () const { return 42 ; }
A< _T > & operator += ( _T r ) { return *this ; }
};
int main() {
A< int > e, f ;
e += f ;
return 0 ;
}
这两个代码有什么区别?它们不应该是等价的吗?
这是用gcc 4.4.7-4编译的。
答案 0 :(得分:6)
第一个示例无法编译,因为模板参数推断不会进行任何转换。与
template <typename _T >
A< _T > & operator += ( A< _T > & l, _T r ) { return l ; }
l
和r
都有助于确定_T
是什么。当您执行e += f
时,编译器会_T
int
l
为r
,A<int>
必须为f
,因为这是_T
类型r
。由于它们不匹配,因此无法编译。
在第二个代码中,没有模板参数推断。编译器知道类的实例化中_T
是什么,所以它需要做的就是将传递给display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex;
的内容转换为parent.layout(true, true);
。
我还建议你不要习惯用下划线开始名字。有一些关于它们的规则,如果你违反它们,那么你的程序有未定义的行为,因为它们是为实现保留的。有关详情,请参阅What are the rules about using an underscore in a C++ identifier?
答案 1 :(得分:3)
简短的回答是,您在用户代码中使用了_T
,因此整个程序生成错误,无需诊断;由_
后跟大写字母组成的标识符保留供实现使用。
在这个意义上,两个例子完全相同。
忽略该错误,它们并不相同。
第一个是非会员template
+=
运营商。
第二个是模板类的非模板成员+=
,它采用隐式this
作为其第一个参数。
这些是非常不同的东西。 template
函数模式匹配不进行转换(除了to-base); template
类型的方法可以转换。
在第二种情况下,非模板operator+=
能够将其第二个参数转换为_T
类型。在第一种情况下,模板operator+=
不会尝试转换模式匹配类型。
实际上有第三种可能性,我通常更喜欢这种可能性:
template < class T >
struct A {
operator T () const { return 42 ; }
friend A& operator += ( A& l, T r ) { return l; (void)r; }
};
我们作为朋友免费+=
。这会创建一个非模板+=
,它带有两个参数,因此更加对称。
此类非模板朋友可以通过ADL找到。
另外,它们也有所不同,因为指向一个的指针可以存储在A<_T>& (A<_T>::*)( _T )
中,另一个指针可以存储在A<_T>& (*)(A<_T>&, _T)
中。这些不相同,也不能转换。
答案 2 :(得分:1)
它们不应该是等价的吗?
不,因为在第一个示例中,_T
是模板function parameter which gets deduced,而在第二个示例中,它是已知的。
想象一下编译器扩展了第二个例子:
template <>
class A_int {
public:
operator int () const { return 42 ; }
A< int > & operator += ( int r ) { return *this ; }
};
g ++ 7.0提供了一个很好的错误消息,解释了扣除失败的原因:
deduced conflicting types for parameter '_T' ('int' and 'A<int>')
可能的解决方案/解决方法是添加额外的模板参数:
template <typename _T, typename _U>
A< _T > & operator += ( A< _T > & l, _U r ) { return l ; }
答案 3 :(得分:0)
这有点棘手。在第二种情况下,您的运算符是类模板A
的成员函数,而不是模板本身。当您致电e += f
时,会发现匹配为A<int>::operator += (int)
,该匹配已存在于A<int>
中。存在从A<int>
到int
的隐式转换,因此此重载有效。
在第一种情况下,运算符是一个模板:编译器尝试通过仅从调用站点推导出_T
参数来实例化它。模板参数推导不考虑用户定义的转换,因此推导失败。
解决方案是通过使用不可导入的上下文(例如通过模板的附加间接)来防止第二个参数参与演绎:
template <class T>
struct NonDeduced_ { using type = T; }
template <class T>
using NonDeduced = typename NonDeduced_<T>::type;
template <typename _T >
A< _T > & operator += ( A< _T > & l, NonDeduced<_T> r ) { return l ; }
然后只有第一个参数参与演绎,成功,然后推导出的_T
用于查看第二个参数是否具有可行的转换。