Difference between internal and external overloaded C ++ operator

I have the following code that will not compile complaining that the + = operator does not exist. + = statement declared outside class 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 ; } 

However, if I implement an operator inside class A, the code compiles and works:

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

What is the difference between these two codes? Shouldn't they be equivalent?

This was compiled with gcc 4.4.7-4.

+7
c ++ class operator-overloading templates
source share
4 answers

The first example cannot be compiled because the output of the template argument does not do any conversion. FROM

 template <typename _T > A< _T > & operator += ( A< _T > & l, _T r ) { return l ; } 

Both l and r contribute to determining that _T . When you do e += f , then the compiler gets that _T must be int for l , and it gets r should be A<int> , since this is type f . Since they do not match, it does not compile.

The second code is missing the output of the template argument. The compiler knows that _T is from an instance of the class, so all it needs to do is convert what is passed to r to _T .


I also suggest that you give up the habit of starting names with underscores. There are many rules about them, and if you break them, then your program has undefined behavior, since they are reserved for implementation. See What are the rules for using underscores in a C ++ ID?

+6
source share

A slight short answer: you used _T in the user code, so your entire program is poorly formed and does not require diagnostics; identifiers consisting of _ followed by a capital letter are reserved for use in the implementation.

In this sense, both examples are exactly equivalent.

Ignoring this error, they are not identical.

The first is an operator that is not a member of template += .

The second element is a non-template += template class that takes an implicit this as its first parameter.

These are very different things. template conformance to the function template does not make any transformations (except for basic ones); template methods can convert.

In the second case, the non-template operator+= is able to convert its second argument to the _T type. In the first case, the operator+= pattern will not try to convert while the pattern matching types.

Actually there is a 3rd feature, which I often prefer:

 template < class T > struct A { operator T () const { return 42 ; } friend A& operator += ( A& l, T r ) { return l; (void)r; } }; 

where we do free += as a friend. This creates a non-pattern += that takes two arguments and is therefore more symmetrical.

Such friends without templates can be found through ADL.

living example .

Aside, they also differ because a pointer to one can be stored in A<_T>& (A<_T>::*)( _T ) , the other as A<_T>& (*)(A<_T>&, _T) . They are not identical and cannot be converted between them.

+3
source share

Shouldn't they be equivalent?

No, because in the first example, _T is a template function parameter that is displayed , and in the second example this is already known.

Imagine the second example being extended by the compiler:

 template <> class A_int { public: operator int () const { return 42 ; } A< int > & operator += ( int r ) { return *this ; } }; 

g ++ 7.0 gives a good error message explaining why the output fails:

 deduced conflicting types for parameter '_T' ('int' and 'A<int>') 

A possible solution / workaround is to add an additional template parameter:

 template <typename _T, typename _U> A< _T > & operator += ( A< _T > & l, _U r ) { return l ; } 

wandbox example

+1
source share

This is a bit complicated. In the second case, your operator is a member function of a class A template, not the template itself. When you call e += f , the match occurs as A<int>::operator += (int) , which already exists inside A<int> . There is an implicit conversion from A<int> to int , so this overload is valid.

In your first case, the operator is a template: the compiler tries to initiate it by _T argument only from the call site. The output of the template argument does not take into account user conversions, so the output is not executed.

The solution is to prevent the second parameter from being included in the deduction using a non-deduced context, such as additional indirect access through the template:

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

Then only the first parameter is involved in the output, which succeeds, then the output _T used to see if the second parameter has viable conversions.

0
source share

All Articles