C++ Template Friend Operator Overloading

C++ template friend operator overloading

It's just a warning about a tricky aspect of the language. When you declare a friend function, it is not a member of the class the declaration is in. You can define it there for convenience, but it actually belongs to the namespace.

Declaring a friend function which is not a template, inside a class template, still declares a non-template function in the namespace. It is neither a member of the class, nor itself a template. However, it is generated by the class template.

Generating non-template functions from a template is a bit hazy. For example, you cannot add a declaration for that function outside the class block. Therefore you must define it inside the class block as well, which makes sense because the class template will generate it.

Another tricky thing about friends is that the declaration inside class Float {} does not declare the function in the namespace. You can only find it through argument-dependent meaning overload resolution, i.e. specifying an that an argument has type Float (or a reference or pointer). This is not an issue for operator+, as it is likely to be overloaded anyway, and it will never be called except for with user-defined types.

For an example of a potential issue, imagine you have a conversion constructor Float::Float( Bignum const& ). But Bignum does not have operator+. (Sorry, contrived example.) You want to rely on operator+(Float const&, Float const&) for Bignum addition. Now my_bignum + 3 will not compile because neither operand is a Float so it cannot find the friend function.

Probably, you have nothing to worry about, as long as the function in question is an operator.

Or, you can change the friend to be a template as well. In that case, it must be defined outside the class {} block, and declared before it, instead of needing to be declared and defined inside.

template<int E, int F> // now this is a template!
Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs);

template<int E, int F>
class Float
{
// deduce arguments E and F - this names operator+< E, F >.
friend Float<E, F> operator+<> (const Float<E, F> &lhs, const Float<E, F> &rhs);
};

C++ Template Inner Class Friend Operator Overload

The issue with the code in the question ended up being that template deduction is not attempted on type names nested inside a dependent type (e.g. Outer<Type>::Inner).

This question is essentially a duplicate of Nested template and parameter deducing. A detailed explanation on why this is a problem can be found here.

Overloading friend operator for template class

You declare operator<< as returning an ostream&, but there is no return statement at all in the method. Should be:

template <class T, class U>
ostream& operator<<(ostream& out, Pair<T,U>& v)
{
return out << v.val1 << " " << v.val2;
}

Other than that, I have no problems or warnings compiling your code under Visual Studio 2008 with warnings at level 4. Oh, there are the classical linker errors, but that is easily bypassed by moving the template function definition to the class declaration, as explained in the C++ FAQ.

My test code:

#include <iostream>
using namespace std;

template <class T, class U>
class Pair{
public:
Pair(T v1, U v2) : val1(v1), val2(v2){}
~Pair(){}
Pair& operator=(const Pair&);
friend ostream& operator<<(ostream& out, Pair<T,U>& v)
{
return out << v.val1 << " " << v.val2;
}
private:
T val1;
U val2;
};

int main() {
Pair<int, int> a(3, 4);
cout << a;
}

overloading friend operator for template class

This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.

The extrovert

Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.

template <typename T>
class Test {
template <typename U> // all instantiations of this template are my friends
friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
// Can access all Test<int>, Test<double>... regardless of what T is
}

The introverts

Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).

This can be done in two ways, the simple way being as @Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:

template <typename T>
class Test {
friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
// can access the enclosing Test. If T is int, it cannot access Test<double>
}
};

In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.

The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:

// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );

// Declare the actual templates:
template <typename T>
class Test {
friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
// Can only access Test<T> for the same T as is instantiating, that is:
// if T is int, this template cannot access Test<double>, Test<char> ...
}

Taking advantage of the extrovert

The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:

namespace hacker {
struct unique {}; // Create a new unique type to avoid breaking ODR
template <>
std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
{
// if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
// if Test<T> is an introvert, then I can only mess up with Test<unique>
// which is just not so much fun...
}
}

C++ template class friend operator overloading

Try this function signature instead (not tested):

template <typename T, typename F>
Vector<T> operator*(const F &lhs, const Vector<T>& rhs);

It should allow for statements like:

auto vec = f * other_vec;

for any type F which has a defined operator of the kind:

template <typename F, typename T>
undetermined operator*(const F& lhs, const T &rhs);

where T is the type used for the Vector<T>, and the returned type can implicitly cast to T.

So the following would probably work:

long l;
float f;
int i;
//.......

Vector<float> v1;
v2 = l * v1;
v3 = f * v2;
v4 = i * v3;

How to overload the friend extraction operator ( ) for templates in C++?

The problem has nothing to do with templates.

operator>> modifies the data on the right-hand side, but you declared that parameter as const, so the operator cannot modify it. The compiler error even states that the value to be modified (d.data) is const:

testMain.cpp:46:8: error: no match for 'operator>>' (operand types are 'std::istream {aka std::basic_istream}' and 'const int')

You need to remove the const from the second parameter:

template<class T>
std::istream& operator>>(std::istream&, Demo<T> &);

...

template<class T>
class Demo {
...
public:
...
friend std::istream& operator>> <T>(std::istream &in, Demo<T> &d);
};

...

template<class T>
std::istream& operator>>(std::istream &in, Demo<T> &d) {
in >> d.data;
return in;
}

C++ templates with friend function and operator overloading

If you declare a friend function within a template class, you must either provide the definition within the template class definition, or redeclare it outside the template class.

Declaring it as a friend with in the template class does not declare that function in the enclosing scope.

C++ friend operator overloading with template parameters for nested enums

The reason is that in the first example the operator| is not in the class inside which the enum types are defined. Hence the function is not being found by ADL (see point 4 of the 2nd list on that page):

For arguments of enumeration type, the innermost enclosing namespace
of the declaration of the enumeration type is defined is added to the
set. If the enumeration type is a member of a class, that class is
added to the set.

In the quote above "set" is defined as "the associated set of namespaces and classes" that are used to look up the function.

Hence in the 1st example no operator| is found which returns a Complex type.

When you move the operator| overload outside of the Complex class in the 1st example, you move it into the global namespace. The global namespace is always part of the "set" mentioned above, and hence the overload is found.

In the 2nd example the enums and the operator| function are in the same class, so they are found by ADL.

c++ template friend operator with another similar operator

Preamble with some Standardese

Your friend declaration matches the first of the four clauses from [temp.friend]/1 (other 3 clauses omitted):

14.5.4 Friends [temp.friend]

1 A friend of a class or class template can be a function template or
class template, a specialization of a function template or class
template, or an ordinary (non-template) function or class. For a
friend function declaration that is not a template declaration:

— if
the name of the friend is a qualified or unqualified template-id, the
friend declaration refers to a specialization of a function template,
otherwise

Which names will be found by your friend declaration?

7.3.1.2 Namespace member definitions [namespace.memdef]

3 [...] If the name in a friend declaration is neither qualified nor a
template-id
and the declaration is a function or an
elaborated-type-specifier, the lookup to determine whether the entity
has been previously declared shall not consider any scopes outside the
innermost enclosing namespace. [ Note: The other forms of friend
declarations
cannot declare a new member of the innermost enclosing
namespace and thus follow the usual lookup rules. — end note ]

Because you have several overloads of operator&, partial ordering is required:

14.5.6.2 Partial ordering of function templates
[temp.func.order]

1 If a function template is overloaded, the use of a function template
specialization might be ambiguous because template argument deduction
(14.8.2)
may associate the function template specialization with more
than one function template declaration. Partial ordering of overloaded
function template declarations is used in the following contexts to
select the function template to which a function template
specialization refers:

— when a friend function
declaration (14.5.4)
, an explicit instantiation (14.7.2) or an
explicit specialization (14.7.3) refers to a function template
specialization
.

and the set of candidates is as usual determined by a set of functions that survive argument template deduction:

14.8.2.6 Deducing template arguments from a function declaration [temp.deduct.decl]

1 In a declaration whose declarator-id refers to a specialization of a
function template
, template argument deduction is performed to
identify the specialization to which the declaration refers.
Specifically, this is done for explicit instantiations (14.7.2),
explicit specializations (14.7.3), and certain friend declarations
(14.5.4).

where surviving argument deduction is governed by the infamous SFINAE (Substition failure is not an error) clause that applies only to the immediate context:

14.8.2
Template argument deduction
[temp.deduct]

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, with a diagnostic required, if written using the substituted arguments. [ Note:
If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution
process. — end note ] Only invalid types and expressions in the immediate context of the function type and
its template parameter types can result in a deduction failure.

Applying the Standard to your example

In all variations on your post, argument-dependent-lookup will find two overloads of operator& in the associated globabl namespace of the fvMatrix class template. These overloads then have to play argument-deduction and partial ordering:

  1. first example (code fragment): because you have friend ... operator& <Type> (...), there is no argument deduction but simple substitution of Cmpt=int and Type=int, which yields an invalid type for product<int>::type inside product2. This is not in the immediate context and therefore a hard error. Removing the Tensor class template of course also removes the error.
  2. second example: as above but with typename product<Cmpt>::type instead of product2<Cmpt::type as the return type of the operator& on Tensor<Cmpt>. Here, the invalid type is in the immediate context, and you get a SFINAE soft error and the valid operator& for fvMatrix<Type> is selected.
  3. third example: as the first but with friend ... operator& <> (...). This requires argument deduction and now the original operator& on Tensor with the product2::type return type is actually harmless because argument-deduction itself fails (there is no template Cmpt that can make Tensor<Cmpt> equal to fvMatrix<int>) and there is no substition that can yield the hard error.

The best way to avoid these subtleties

Because the root cause is polluttion of the global namespace by unrelated operator overloads, the cure is simple: wrap each class template inside its own namespace! E.g. Tensor<Cmpt> into namespace N1 and fvMatrix<Type> into namespace N2. Then the friend declaration inside fvMatrix will not find the operator& for Tensor<Cmpt> and all works fine.



Related Topics



Leave a reply



Submit