the code below shows some interesting behavior:
#include <iostream>
using namespace std;
template<class T>
class B{
public:
void foo(B<T> &x)const;
template<class F> void foo(F f);
};
template<typename T> void B<T>::foo(B<T> &x)const{cout<<"foo_B"<<endl;}
template<typename T> template<typename F> void B<T>::foo(F f){cout<<"foo_F"<<endl;}
int main(){
B<int> a;
B<int> b;
b.foo(a);
b.foo([](){;});
return(0);
}
my expected output is
foo_B
foo_F
but the actual output is
foo_F
foo_F
it depends on whether void foo(B<T> &x)
is declared const
. If const
is omitted the output is as expected.
Further, if const
is added to void foo(F f)
the output is as expected as well.
However, void foo(B<T> &x)
will not change the this
, whereas void foo(F f)
will change this
. So the current layout is the one required.
Any idea how to resolve this without dropping const
is much appreciated.
The issue here is that since void foo(B<T> &x)const;
is const qualified, It would have to const qualify the object you are calling the function on. This isn't as exactly as a match as template<class F> void foo(F f);
provides as it doesn't need to do that const qualification. That is why it is used for both calls.
You can fix this by also const qualifying the template version like:
#include <iostream>
using namespace std;
template<class T>
class B{
public:
void foo(B<T> &x)const;
template<class F> void foo(F f)const;
};
template<typename T> void B<T>::foo(B<T> &x)const{cout<<"foo_B"<<endl;}
template<typename T> template<typename F> void B<T>::foo(F f)const{cout<<"foo_F"<<endl;}
int main(){
B<int> a;
B<int> b;
b.foo(a);
b.foo([](){;});
return(0);
}
Which will print
foo_B
foo_F
Another option would be to use SFINAE to constrain the template version from excepting B<T>
's. That would look like
#include <iostream>
using namespace std;
template<class T>
class B{
public:
void foo(B<T> &x)const;
template<class F, std::enable_if_t<!std::is_same_v<B<T>, F>, bool> = true>
void foo(F f);
};
template<typename T> void B<T>::foo(B<T> &x)const{cout<<"foo_B"<<endl;}
template<typename T> template<class F, std::enable_if_t<!std::is_same_v<B<T>, F>, bool>>
void B<T>::foo(F f){cout<<"foo_F"<<endl;}
int main(){
B<int> a;
B<int> b;
b.foo(a);
b.foo([](){;});
return(0);
}
and has the same output as the first example.