deleted friend declaration declares a non-template function - c++

I want to delete a specific overloaded friend function in a class.
The below code compiles, and works in the way intended.
However, I get the following warning (g++, c++11,14,17):
edit: Warning is present using gcc9.3 and older, but not for gcc10
warning: friend declaration 'bool operator==(const MyType<BaseT>&, const BaseT&)' declares a non-template function [-Wnon-template-friend]
6 | friend bool operator==(const MyType<BaseT> &lhs, const BaseT &rhs) = delete;
| ^~~~~~
new.cpp:6:72: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
Can someone shed light on the warning? Why does it apply for the deleted function, but not the implemented one?
template <typename BaseT> struct MyType {
BaseT v;
explicit MyType(BaseT tv) : v(tv) {}
explicit operator BaseT() const { return v; }
friend bool operator==(const MyType<BaseT> &lhs, const BaseT &rhs) = delete;
friend bool operator==(const MyType<BaseT> &lhs, const BaseT &&rhs) {
return lhs.v == rhs;
}
};
int main(){
MyType<int> a{3};
int b{3};
// a==b; //Fails to compile: good. Do not allow comparisons between different types
a==3; //Compiles fine: good. Allow literal comparison, without implicit constructor
}

Related

operator==() code compiles w/C++17 but not C++20

This code snippet
#include <stdlib.h>
struct Base { };
template<typename T>
inline bool operator==(const T&, const Base&)
{
return true;
}
template<typename T>
inline bool operator!=(const T& lhs, const Base& rhs)
{
return !(lhs == rhs);
}
struct A : public Base
{
bool operator==(const A&) const { return true; }
};
struct A_1 final : public A { };
int main()
{
const A a;
const A_1 a_1;
if (a_1 != a) {}
return EXIT_SUCCESS;
}
Compiles without errors in C++17 (Visual Studio 2022).
(See C++17 operator==() and operator!=() code fails with C++20 for a more detailed example; note in that case the code compiles.)
Trying to build the same code with C++20 generates three compiler errors:
error C2666: 'operator !=': 3 overloads have similar conversions
message : could be 'bool A::operator ==(const A &) const' [rewritten expression '!(x == y)']
message : or 'bool A::operator ==(const A &) const' [synthesized expression '!(y == x)']
message : or 'bool operator !=<A_1>(const T &,const Base &)'
Yes, I understand that C++20 will synthesize operator!= and the like ... but shouldn't existing C++17 code still compile with C++20? How can I fix things so that the same code compiles with both C++17 and C++20 and generates identical results?
Making changes to derived class could be difficult as that code could be elsewhere (this is actually library code); it would be much preferred to make changes to Base.
This is because all of the functions are now candidate, and before they weren't.
This means that suddently, the operator== in A is a candidate for a_1 != a. The compiler is now allowed to invert the arguments, change a == b to !(a != b) and vice versa, and even change the order to b == a.
The code result in an ambiguous call since bool operator==(const A&); in A supports a_1 != a, by inverting the operation to !(a_1 == a) and changing parameter order to finally have !(a == a_1) which is a candidate.
There are multiple solution to this.
One is to simply make one candidate better by inheriting the function:
struct A_1 final : public A { using A::operator==; };
The other would be to restraint the operator to work only with A:
struct A : public Base
{
friend bool operator==(std::same_as<A> auto const&, std::same_as<A> auto const&) { return true; }
};
Another solution not requirering changing A or A_1 would be to add an overload that is always an ideal candidate inside Base as a friend function.
The whole code becomes this in C++20:
struct Base {
friend bool operator==(std::derived_from<Base> auto const&, std::derived_from<Base> auto const&) { return true; }
};
struct A : Base {};
struct A_1 final : A {};
You can remove the template function in the global namespace, and you can also remove the function in the derived classes too.
You are not required to remove the function in A, but it won't be call and the resulting code will be quite surprising still.
However, note that your code was broken before.
This is the compiler output for code using the operator== in C++17:
int main()
{
const A a;
const A_1 a_1;
if (!(a_1 == a)) {}
return EXIT_SUCCESS;
}
Compiler output:
<source>: In function 'int main()':
<source>:26:15: error: ambiguous overload for 'operator==' (operand types are 'const A_1' and 'const A')
26 | if (!(a_1 == a)) {}
| ~~~ ^~ ~
| | |
| | const A
| const A_1
<source>:17:10: note: candidate: 'bool A::operator==(const A&) const'
17 | bool operator==(const A&) const { return true; }
| ^~~~~~~~
<source>:5:13: note: candidate: 'bool operator==(const T&, const Base&) [with T = A_1]'
5 | inline bool operator==(const T&, const Base&)
| ^~~~~~~~
ASM generation compiler returned: 1
C++20 will just accept less code that has a surprising behaviour.
It's fine if you remove A::operator==.
There's really no point to A::operator==, as the template is saying that everything is equal to a Base.
but shouldn't existing C++17 code still compile with C++20?
The committee works hard to minimise breaking changes, but they don't promise there will be none. In this case it was felt that having == be an equivalence relation was more important than maintaining existing behaviour. As the linked question notes, polymorphic equality testing is often a source of bugs.
C++ extends overload resolution for relational operator to include swapped arguments, which is in the diagnostic message:
GCC marks the candidate "(reversed)" and
Clang mentions (with reversed parameter order)
So you need fewer operators. In fact, you could consider overloading / defaulting the three-way comparison operator (<=>) instead.

Overload resolution for template operators in namespaces

I ran into a bit of trouble with template operator overloads when entering namespaces. Consider addition of arrays:
// overloads.hpp
#include <array>
namespace mylib {
template <size_t N>
using DoubleArray = std::array<double,N>;
template <size_t N>
DoubleArray<N> operator+( const DoubleArray<N>& lhs, const DoubleArray<N>& rhs ) {return DoubleArray<N>();}
}
Testing this in namespace mylib works as intended.
// test.cpp
#include "overloads.hpp"
namespace mylib {
void test()
{
DoubleArray<3> a({1.0,0.0,0.0});
DoubleArray<3> b({0.0,1.0,0.0});
DoubleArray<3> c(a+b); // <-- ok
}
}
Now suppose that I have a Complex class in namespace mylib::mysublib that has its own operator+ and a constructor from DoubleArray (this constructor has to be explicit to prevent implicit conversion):
// nested.cpp
#include "overloads.hpp"
namespace mylib {
namespace mysublib {
struct Complex
{
Complex() {};
explicit Complex( const DoubleArray<2>& components );
DoubleArray<2> _components;
};
Complex operator+(const Complex& rhs, const Complex& lhs) {return Complex();}
void testNested()
{
DoubleArray<2> a({1.0,0.0});
DoubleArray<2> b({0.0,1.0});
DoubleArray<2> c(a+b); // <-- no match for ‘operator+’
DoubleArray<2> d( mylib::operator+(a,b) ); // <-- ok
}
}
}
Error message:
error: no match for ‘operator+’ (operand types are ‘mylib::DoubleArray<2> {aka std::array<double, 2>}’ and ‘mylib::DoubleArray<2> {aka std::array<double, 2>}’)
DoubleArray<2> c(a+b); // <-- no match for ‘operator+’
Why can't the overloaded operator be found when called from the nested namespace? The whole point of overloading (in this example) would be a clean syntax. Any ideas on how to get this working, or if it's even possible?
The operator+ of Complex can be declared as friend function in Complex, which does not pollute the global namespace. Your example should compile after the following change.
struct Complex {
Complex(){};
explicit Complex(const DoubleArray<2>& components);
DoubleArray<2> _components;
friend Complex operator+(const Complex& rhs, const Complex& lhs) { return Complex(); }
};
According to C++ standard working draft N4140,
When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded.
In your case, the two operator+ functions are declared in different namespace and thus are not qualified for overload resolution.
When compiler finds the first match Complex operator+(const Complex& rhs, const Complex& lhs), DoubleArray cannot be implicitly converted to Complex. Therefore, you got the no match for ‘operator+’ error.
Replace your third code by
namespace mylib {
namespace mysublib {
struct Complex
{
Complex() {};
explicit Complex( const DoubleArray<2>& components );
DoubleArray<2> _components;
};
//Complex operator+(const Complex& rhs, const Complex& lhs) {return Complex();}
void testNested()
{
DoubleArray<2> a({1.0,0.0});
DoubleArray<2> b({0.0,1.0});
DoubleArray<2> c(a+b); // <-- no match for ‘operator+’
DoubleArray<2> d( mylib::operator+(a,b) ); // <-- ok
}
}
}
, then It compiles.
The definition Complex operator+(const Complex& rhs, const Complex& lhs) {return Complex();} hides the intented operator

How to fix compilation error with gcc8 while specializing swap function/

I'm trying to compile code which specialize the function std::swap for my class.
But I'm facing an issue that occurs because of rvalue constructor (it works when I comment the line)
I am compiling with g++ (GCC) 8.2.1 20180905 (Red Hat 8.2.1-3)
and g++ -std=c++14 -Wall those options
#include <iostream>
#include <utility>
namespace ns1
{
class Foo
{
public:
Foo();
Foo(const Foo& that) {};
Foo(Foo&& that) {}; // <--- work when commented
~Foo();
void swap(Foo &that) {};
};
inline void swap(Foo &lhs, Foo &rhs)
{
lhs.swap(rhs);
}
} // namespace ns1
namespace std {
template <>
inline void swap(ns1::Foo &lhs, ns1::Foo &rhs)
{
lhs.swap(rhs);
}
} // namespace std
I have the following error message :
error: template-id 'swap<>' for 'void std::swap(ns1::Foo&, ns1::Foo&)' does not match any template declaration
inline void swap(ns1::Foo &lhs, ns1::Foo &rhs)
^~~~
In file included from /opt/rh/devtoolset-8/root/usr/include/c++/8/string:52,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/locale_classes.h:40,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/ios_base.h:41,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/ios:42,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/ostream:38,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/iostream:39,
from toto.cpp:1:
/opt/rh/devtoolset-8/root/usr/include/c++/8/bits/basic_string.h:6276:5: note: candidates are: 'template<class _CharT, class _Traits, class _Alloc> void std::swap(std::basic_string<_CharT, _Traits, _Alloc>&, std::basic_string<_CharT, _Traits, _Alloc>&)'
swap(basic_string<_CharT, _Traits, _Alloc>& __lhs,
^~~~
In file included from /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/stl_algobase.h:64,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/char_traits.h:39,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/ios:40,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/ostream:38,
from /opt/rh/devtoolset-8/root/usr/include/c++/8/iostream:39,
from toto.cpp:1:
The error you are seeing is because providing a user defined move-constructor prevents compiler from synthesizing move-assignment operator, therefore making your class non move-assignable.
As per cppreferece, T in swap must be move-assignable.
To fix it, provide move-assignment operator as well, demo
Foo& operator=(Foo&& other) {return *this;}
Foo& operator= (const Foo& other) { return *this; }
Also take a look at rule of five
Maybe instead of extending std it should be better to rely on ADL+using std::swap;.
Your example is very similar to this guideline.
(http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c165-use-using-for-customization-points)
Another guideline tells that we should prefer overload to template
specialisation
(http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#t144-dont-specialize-function-templates)
but unfortunately it is undefined behavior in std
(https://en.cppreference.com/w/cpp/language/extending_std)

C++ class (with set) storing a generic template class...compilation problems

So I'm still some what new to C++ programming and very new at templates. I am trying to make a basic template class (a node if you will) that holds some generic data and a double. I then want to make another class contain a set of the previously mentioned template class.
Im having trouble with the less-than operator as its going to server as my comparator.
Node&Tree.h
#ifndef _POINTNODE_H_
#define _POINTNODE_
#include <set>
template<typename T>
class PointNode {
public:
PointNode(double p){ m_point = p;}
~PointNode();
bool operator < (const &PointNode<T> p1) const;
private:
const double m_point;
T *m_data;
};
template <typename T>
class PointTree {
public:
PointTree();
~PointTree();
private:
std::set<PointNode<T> > tree;
};
#endif
Node&Tree.cpp
#inlcude "Node&Tree.h"
#include <set>
template<typename T>
bool PointNode<T>:: operator < (const &PointNode<T> p1) const{
return m_point < p1.m_point;
}
Im getting the folowing errors
Node&Tree.cpp:5:39: error: ISO C++ forbids declaration of ‘parameter’ with no type [- fpermissive]
Node&Tree.cpp:5:39: error: expected ‘,’ or ‘...’
Node&Tree.cpp:5:6: error: prototype for ‘bool PointNode<T>::operator<(const int&) const’ does not match any in class ‘PointNode<T>’
Node&Tree.h:15:8: error: candidate is: bool PointNode<T>::operator<(const int&)"
This is largely unimplemented but I just wanted to get the basics to compile at least... And any pointers on the code or if you think I'm going about this all wrong please tell me!
Any help would be amazing!
bool PointNode<T>:: operator < (const &PointNode<T> p1) const
should be:
bool PointNode<T>:: operator < (const PointNode<T>& p1) const
You put the reference & in the wrong position, so you have that forbids declaration of parameter error. Another place has the same error.
bool operator < (const &PointNode<T> p1) const;
should be
bool operator < (const PointNode<T>& p1) const;
Make PointNode object as reference
bool operator < (const PointNode<T>& p1) const;
And its defination
template<typename T>
bool PointNode<T>:: operator < (const PointNode<T>& p1) const{
return m_point < p1.m_point;
}
this will fix the problem.

Some compiler errors concerning an overloaded operator on a template in c++

I have some code with a few errorr I do not understand how to fix at all. I have asked my professor and TA, and consulted the internet with no luck, apart from understanding more precisely what the errors mean. From what I can tell, the compiler is either confusing my overloaded operator with built in operators, or it is not recognizing it as an overloaded operator at all.
I am getting the following errors and warnings:
||=== project 4, Debug ===|
\project 4\forest.h|13|warning: friend declaration 'Forest<NODETYPE>& operator+(Forest<NODETYPE>&, Forest<NODETYPE>&)' declares a non-template function|
\project 4\forest.h|13|note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) |
\project 4\forest.h|14|warning: friend declaration 'std::ostream& operator<<(std::ostream&, const Forest<NODETYPE>&)' declares a non-template function|
\project 4\forest.h|15|warning: friend declaration 'std::istream& operator>>(std::istream&, Forest<NODETYPE>&)' declares a non-template function|
\project 4\main.cpp||In function 'int main()':|
\project 4\main.cpp|21|error: ambiguous overload for 'operator>>' in 'file >> intForest'|
c:\program files (x86)\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\include\c++\istream|119|note: candidates are: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_istream<_CharT, _Traits>& (*)(std::basic_istream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>] <near match>|
c:\program files (x86)\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\include\c++\istream|123|note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>] <near match>|
c:\program files (x86)\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\include\c++\istream|130|note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>] <near match>|
||=== Build finished: 1 errors, 3 warnings ===|
When I try to compile my code. The relevant code segments are as follows:
(in forest.h)
template< typename NODETYPE > class Forest
{
public:
friend Forest<NODETYPE>& operator+(Forest<NODETYPE>&, Forest<NODETYPE>&);
friend ostream& operator<<(ostream&, const Forest<NODETYPE>&);
friend istream& operator>>(istream&, Forest<NODETYPE>&);
Forest();
Forest( const Forest& otherForest);
~Forest();
void nodes(int&) const;
private:
ForestNode<NODETYPE> *root;
ForestNode<NODETYPE> *getNewNode( const NODETYPE &);
};
(in forest.cpp)
template<typename NODETYPE> istream& operator>>(istream& file, const Forest<NODETYPE>& f1)
{
istream file2 = file;
int nodeCount = 0;
string blah = ' ';
while(getline(file2,blah))
{
nodeCount++;
}
ForestNode<NODETYPE> *forestNodeArray[nodeCount];//holds pointers to last node of depth i
getline(file,*f1.root.tag);
forestNodeArray[0] = &(*f1.root);
inputHelper(file, 0, *f1.root, forestNodeArray, nodeCount);
return file;
}
(and finally, in main.cpp)
int main()
{
Forest < char > intForest();
filebuf fb;
fb.open ("forest1.txt",ios::in);
istream file(&fb);
cout << typeid(intForest).name() << endl;
cout << typeid(file).name() << endl;
file >> intForest;
fb.close();
}
Any help would be greatly appreciated.
EDIT: Thanks to alex and alf, I understand why they were not considered template functions. It's quite obvious in retrospect, I was just set on those signatures. Anyway, I still get the error about the ambiguous operator. Why does the compiler not recognize my operator and use it, instead of trying to decide between 3 built in versions of the operator that could not possibly have one parameter as Forest?
The second error is in this line:
Forest < char > intForest();
This may be surprising at first, but that line is not declaring a variable of type Forest<char>, but rather a function that takes no arguments and returns a Forest<char>.
Just remove the parenthesis from the declaration:
Forest < char > intForest;
On the first warning, which is already explained (the function declared as friend is not a template, and that means that you will manually have to implement it for each type you use to instantiate Forest<> with (probably you don't want that). Also note that declaring the templated operator+ and then making the template a friend as in #Alf P. Steinbach answer means that a Forest<int> will be a friend of a Forest<double>, which might not be what you need. #Alex proposal in the comment will only make a particular instantiation of the template a friend, which is probably closer to what you want, but you need to declare the templated operator before the template class, and for that you will need to forward declare the template class...
A common pattern for friend free functions in templates is defining the function in place:
template <typename T>
class Forest {
// ...
friend Forest& operator+( Forest const & lhs, Forest const & rhs ) [1]
{
// implementation here
}
}
// [1] Note that the arguments are const references (read only), and also note that
// you do not need to provide the type argument inside the template declaration
This allows you to define it as a non templated function and at the same time have the compiler instantiate the function for you. Also, it is usually simpler to also define member methods inlined in the class definition when dealing with templates. It makes life simpler, and after all in most cases you do need to provide the implementation in the (same) header file.
Yet, when defining binary operators a better approach is to define operator+= as a member method, and then you can easily define operator+ as a non-friend free function. The pattern would be:
struct test {
test& operator+=( test const & );
};
test operator+( test lhs, test const & rhs ) { // [2]
lhs += rhs;
return lhs;
}
// [2] Note that the first argument is by value, and the second by const reference
// This means that the compiler will copy the first argument for you, and you
// are free to modify it with the += operator and return the copy.
Now, the most tricky part is mixing the previous two advices. To be able to define operator+ that is a free function inside the template definition, a common trick is to make it friend even if that is not required for access reasons:
template <typename T>
class Forest {
Forest& operator+=( Forest const & ) {
// implemenation
}
friend Forest operator+( Forest lhs, Forest const & rhs ) { // [3]
return lhs+=rhs;
}
};
// [3] friendship only to allow us to define the free function inside the template
// declaration.
The (first) warning tells you that the befriended function is not a template.
And it isn't.
Think about how you would implement it. It would entail writing one such function for each possible NODE_TYPE.
There are several ways to fix that.
One way is to befriend a function template, like so:
template< class Type >
struct S
{
template< class U > friend void foo( S<U> );
};
template< class Type >
void foo( S< Type > x ) {}
int main()
{}
Cheers & hth.,
learning C++,I also encountered the same problem.But I had solved it for another way!
template <class NODETYPE> class Forest;
template <class NODETYPE> Forest<NODETYPE>& operator+ (Forest<NODETYPE>&, Forest<NODETYPE>&);
template <class NODETYPE> ostream& operator<<(ostream&, const Forest<NODETYPE>&);
template <class NODETYPE> istream& operator>> (istream&, Forest<NODETYPE>&);
template<class NODETYPE>
template< typename NODETYPE >
class Forest
{
public:
friend Forest<NODETYPE>& operator+ <>(Forest<NODETYPE>&, Forest<NODETYPE>&);
friend ostream& operator<< <>(ostream&, const Forest<NODETYPE>&);
friend istream& operator>> <>(istream&, Forest<NODETYPE>&);
Forest();
Forest( const Forest& otherForest);
~Forest();
void nodes(int&) const;
private:
ForestNode<NODETYPE> *root;
ForestNode<NODETYPE> *getNewNode( const NODETYPE &);
};