Based on the answer in Implicit conversion when overloading operators for template classes I was able to write the following code that works perfectly fine (simplified example):
namespace my_library {
template <typename T>
struct Number {
T n;
inline Number(T n) : n(n) { }
friend Number<T> operator+(const Number<T> &a, const Number<T> &b) {
return Number<T>(a.n + b.n);
}
};
}
int main() {
return (int) (4 + my_library::Number<double>(3)).n; // returns 7
}
All I want to do is make it so that operator+ is not inlined within the definition of Number (but stays in the header file) while everything still works the same way - see the expression in the main function. Note that it requires that the integer 4 gets implicitly converted to double and then to Number<double>. I followed a comment linking to Binary operator overloading on a templated class but that solution did not work - the overloaded operator is no longer matched. Is there any way to make this work and have the body of the operator outside the struct definition? (Without complicating the operator interface or adding more overloads - in those cases I'd rather just keep it inlined.)
You can do it like this:
template <typename T>
struct Number {
// ...
template <class U>
friend Number<U> operator+(const Number<U> &a, const Number<U> &b);
};
template <typename U>
Number<U> operator+(const Number<U> &a, const Number<U> &b) {
return Number<U>(a.n + b.n);
}
In this example, operator+ will be a friend of all Number specializations even when the types don't match. This might be a broader friendship than you intended, but is the simplest way to achieve your objective of moving the definition of operator+ outside the definition of Number. Note that when a friend is not defined inline, you lose the benefit of having it as a "hidden friend". (A hidden friend can only be found via argument-dependent lookup, which means that the compiler will typically not have to consider it as a candidate function in most cases where it is not intended to be used.)
If you want each specialization of operator+ to only be a friend of one particular Number specialization where the types match, it is more complicated. You have to forward-declare the operator+ template in order to prevent the friend declaration from declaring a non-template function, and you also have to forward-declare Number so that it can be used in the operator+ forward declaration:
template <typename T> struct Number;
template <typename T>
Number<T> operator+(const Number<T>&, const Number<T>&);
template <typename T>
struct Number {
// ...
friend Number<T> operator+<>(const Number<T>&, const Number<T>&);
};
template <typename T>
Number<T> operator+(const Number<T> &a, const Number<T> &b) {
return Number<T>(a.n + b.n);
}
Related
I have a problem to overload the << stream operator and I don't find the solution :
template<class T, unsigned int TN>
class NVector
{
inline friend std::ostream& operator<< (
std::ostream &lhs, const NVector<T, TN> &rhs);
};
template<class T, unsigned int TN>
inline std::ostream& NVector<T, TN>::operator<<(
std::ostream &lhs, const NVector<T, TN> &rhs)
{
/* SOMETHING */
return lhs;
};
It produces the following error message:
warning : friend declaration ‘std::ostream& operator<<(std::ostream&, const NVector&)’ declares a non-template function [-Wnon-template-friend]
error: ‘std::ostream& NVector::operator<<(std::ostream&, const NVector&)’ must take exactly one argument
How to solve that problem ?
Thank you very much.
There are two different issues in your code, the first is that the friend declaration (as the warning clearly says, maybe not so clear to understand) declares a single non-templated function as a friend. That is, when you instantiate the template NVector<int,5> it declares a non-templated function std::ostream& operator<<(std::ostream&,NVector<int,5>) as a friend. Note that this is different from declaring the template function that you provided as a friend.
I would recommend that you define the friend function inside the class definition. You can read more on this in this answer.
template <typename T, unsigned int TN>
class NVector {
friend std::ostream& operator<<( std::ostream& o, NVector const & v ) {
// code goes here
return o;
}
};
Alternatively you can opt for other options:
declare the operator<< template as a friend (will grant access to any and all instantiations of the template),
declare a particular instantiation of that template as a friend (more cumbersome to write) or
avoid friendship altogether providing a public print( std::ostream& ) member function and calling it from a non-friend templated operator<<. I would still opt to befriend the non-template function an provide the definition inside the templated class.
The second issue is that when you want to define an operator outside of the class of the left hand side argument, the operator is a free function (not bound to a class) and thus it should not be qualified:
template<class T, unsigned int TN>
inline std::ostream& operator<<(std::ostream &lhs, const NVector<T, TN> &rhs)
{
/* SOMETHING */
return lhs;
};
I am trying to create some friends functions for my class template. I know you have to declare them outside the class first but I am having some weird results.
...
template <typename T>
class Matrix;
template <typename T>
std::ostream& operator<<(std::ostream& os, const Matrix<T>& mtrx);
template <typename T>
std::istream& operator>>(std::istream& is, const Matrix<T>& mtrx);
template <typename T>
Matrix<T> operator*(const T& t, const Matrix<T>& mtrx);
template <typename T>
class Matrix
{
public:
friend std::ostream& operator<< <>(std::ostream&, const Matrix&);
friend std::istream& operator>> <>(std::istream&, const Matrix&);
Matrix operator*(const T&) const;
friend Matrix<T> operator* <>(const T& t, const Matrix& mtrx);
};
...
The two stream operators work fine for me, but the multiplication operator has issues. For some reason the compiler (Visual Studio) seems to think I am trying to declare a data member and keeps throwing a bunch of errors. Like
C2460 uses Matrix which is being defined;
C2433 friend not permitted on data declarations;
c2473 looks like a function definition, but there is no parameter list.
As far as I can tell, the implementation is the same as for the stream operators which work. Also, if I rename operator* to something else, like foo, it works fine.
Now, I don't actually need to make this function a friend, my definition just reverses order of arguments and calls the member function version. But I still want to know what is going on and what is wrong.
There are two things at play here.
First
A C++ compiler is permitted (but not required!) to diagnose errors at the time the template is parsed when all of the instantiations of the template would produce that error.
This means that since you've not instantiated class template Matrix, compilers are free to diagnose the error. For example, the given program is rejected by msvc but accepted(compiled) by gcc and clang in C++20. Demo.
So this is not msvc bug. For more detailed explanation of this, refer to Template class compiles with C++17 but not with C++20 if unused function can't be compiled.
Note also that if you do instantiate Matrix for some template argument say by writing Matrix<int>, then the program is also rejected by gcc. Demo. See reason why this is rejected.
Important
The important thing to note here is that the exact same program is rejected by all compilers with C++17 irrespective of whether you instantiate Matrix for a given template argument or not. Demo
Second
Now, lets see the reason for the program being rejected with all compilers in C++17.
In your given program when you try to befriend a specialization of the function template operator*, the compiler starts lookup(looking up) for an operator* and it finds the non-template overloaded version and so stops looking further. Now, since it found a nontemplate version which cannot be specialized, it gives the error in all compilers.
Lets, look at a contrived example:
template<typename T> void f(T){
}
template <typename T>
class Matrix
{
public:
int f(const T&) const; //#1
friend void f<>(const Matrix& mtrx); //lookup find #1 and stops looking and so gives error
};;
int main()
{
Matrix<int> m;
}
To solve the above error, it we've to reorder the declarations so that the lookup for operator* isn't found when befriending the specialization.
This means we have to change the above program to:
template<typename T> void f(T){ //#2
}
template <typename T>
class Matrix
{
public:
friend void f<>(const Matrix& mtrx); //lookup find #2
int f(const T&) const; //OK now
};;
int main()
{
Matrix<int> m;
}
Summary
To solve the error you're getting you've to reorder the declarations for operator* like shown below:
template <typename T>
class Matrix
{
public:
friend std::ostream& operator<< <>(std::ostream&, const Matrix&);
friend std::istream& operator>> <>(std::istream&, const Matrix&);
//reordered
friend Matrix<T> operator* <>(const T& t, const Matrix& mtrx);
Matrix operator*(const T&) const;
};
Demo
Is it considered a good practice to use friend to define global functions within the class definition, even when the access to private members is not needed. For example
template<typename T>
class A {
public:
A(T v);
T value() const;
friend A operator+(T n, const A& a) {
return A(a.value() + n);
}
};
instead of
template<typename T>
class A {
public:
A(T v);
T value() const;
};
template<typename T>
A<T> operator+(T n, const A<T>& a) {
return A<T>(a.value() + n);
}
even though operator+ only uses value() which is public.
Is this commonly done, us is it not recommended?
There's one major advantage to friend here. When we define:
friend A operator+(T, const A&);
This is not a function template. This is just a function - a special one that can only be found by ADL. But since it's not a function template, conversions still can happen. On the flip side:
template <class T>
A<T> operator+(T, const A<T>&)
is a normal old function template with all of the normal rules regarding template type deduction.
Why does this matter? Consider:
A<double> a(4.2);
5 + a;
In the first case, this is perfectly fine. We find operator+(double, const A<double>&), the 5 gets converted to 5.0, which is an allowed conversion, and we get back A<double>(9.2).
In the second case, template deduction fails because the T deduces to different types for the two arguments. Hence, the code is ill-formed.
I have a problem to overload the << stream operator and I don't find the solution :
template<class T, unsigned int TN>
class NVector
{
inline friend std::ostream& operator<< (
std::ostream &lhs, const NVector<T, TN> &rhs);
};
template<class T, unsigned int TN>
inline std::ostream& NVector<T, TN>::operator<<(
std::ostream &lhs, const NVector<T, TN> &rhs)
{
/* SOMETHING */
return lhs;
};
It produces the following error message:
warning : friend declaration ‘std::ostream& operator<<(std::ostream&, const NVector&)’ declares a non-template function [-Wnon-template-friend]
error: ‘std::ostream& NVector::operator<<(std::ostream&, const NVector&)’ must take exactly one argument
How to solve that problem ?
Thank you very much.
There are two different issues in your code, the first is that the friend declaration (as the warning clearly says, maybe not so clear to understand) declares a single non-templated function as a friend. That is, when you instantiate the template NVector<int,5> it declares a non-templated function std::ostream& operator<<(std::ostream&,NVector<int,5>) as a friend. Note that this is different from declaring the template function that you provided as a friend.
I would recommend that you define the friend function inside the class definition. You can read more on this in this answer.
template <typename T, unsigned int TN>
class NVector {
friend std::ostream& operator<<( std::ostream& o, NVector const & v ) {
// code goes here
return o;
}
};
Alternatively you can opt for other options:
declare the operator<< template as a friend (will grant access to any and all instantiations of the template),
declare a particular instantiation of that template as a friend (more cumbersome to write) or
avoid friendship altogether providing a public print( std::ostream& ) member function and calling it from a non-friend templated operator<<. I would still opt to befriend the non-template function an provide the definition inside the templated class.
The second issue is that when you want to define an operator outside of the class of the left hand side argument, the operator is a free function (not bound to a class) and thus it should not be qualified:
template<class T, unsigned int TN>
inline std::ostream& operator<<(std::ostream &lhs, const NVector<T, TN> &rhs)
{
/* SOMETHING */
return lhs;
};
How can I make the insertion (<<) and/or extraction (>>) operator overloaded in a template class WITHOUT making it inline. I would like to have the << or >> operator as a friend class.
I know how to make it inline
example of inline in a matrix class
friend ostream& operator<<(ostream& ostr, const Matrix<T>& inputMatrix)
{
...
// create the ostr
return ostr;
}
but I'd like to have the code outside of the templateclass definition.
g++ told me to add <> after the function name so I did but when I tried to instansiate a matrix of type SOMETYPE it gave me an error that it didn't know how to extract or insert for that type.
If you really want to define the operator externally and befriend only the operator instantiation that coincides in type with this template instantiation, the correct syntax is:
template <typename T> class test; // forward declare template class
template <typename T> // forward declare the templated operator
std::ostream& operator<<( std::ostream&, test<T> const & );
template <typename T>
class test { // define the template
friend std::ostream& operator<< <T>( std::ostream&, test<T> const & ); // befriend
};
template <typename T> // define the operator
std::ostream& operator<<( std::ostream& o, test<T> const & ) {
return o;
}
In most cases it is not worth the hassle to pull the definition out of the class, considering that you still need to provide it in a header and the extra work required.
Also note that there are slight differences for the compiler regarding lookup. In the case where the function is inlined inside the class definition, the compiler will not find that function unless one of the arguments is actually of the type of the template, so it effectively reduces the visibility and the amount of work that the compiler has to do (if the templated operator<< is defined outside of the class, the compiler will find it as a candidate for overload resolution in all places where it finds a << b, only to discard it in all cases where the second argument is not a test<T> (and it will show the templated operator as a candidate in all error messages where it cannot match operator<<, which is a long enough list already).
Try something like:
template <typename T> class Matrix;
template <typename T> std::ostream& operator<<(std::ostream& ostr, const Matrix<T>& m);
template <Typename T>
class Matrix
{
public:
friend ostream& operator<< <T> (ostream& ostr, const Matrix<K>& inputMatrix);
};
// This must be in the same translation unit as the class definition!
template<typename T>
ostream& operator<<(ostream& ostr, const Matrix<T>& inputMatrix)
{
// ...
return ostr;
}
Translation unit reference
re-re-edited to addressed the comments made by aschepler and dribeas.
Place the code in the header, outside the class definition. Or, place it in a .tcc file and include it at the bottom of the header.