virtual method ignored in tempate inheritance - c++

I've tried searching for some explanations about how this exact pattern of inheritance works, but never found anything quite similar, so I hope some of you guys know what's going on.
So here's the behaviour I want to get:
#include <iostream>
template <typename T, typename F = std::less<T>>
class Base
{
protected:
F obj;
public:
virtual bool f(T t1, T t2) { return obj(t1, t2); }
};
template <typename T>
struct Derived : public Base<T, std::less<T>>
{
public:
bool f(T t1, T t2) { return this->obj(t2, t1); }
};
int main()
{
Base<int> b;
std::cout << std::boolalpha << b.f(1, 2) << '\n';
Derived<float> d;
std::cout << std::boolalpha << d.f(1, 2) << '\n';
}
Here I redefine f() in Derived and change how the class behaves. The program prints out
true
false
just like it should.
However, when I try to modify it so that Derived could take a pair but only compare the .first fields like this
#include <iostream>
template <typename T, typename F = std::less<T>>
class Base
{
protected:
F obj;
public:
virtual bool f(T t1, T t2) { return obj(t1, t2); }
};
template <typename T>
struct Derived : public Base<std::pair<T, T>, std::less<T>>
{
public:
typedef std::pair<T, T> pair;
bool f(pair t1, pair t2) { return this->obj(t1.first, t2.first); }
};
int main()
{
Base<int> b;
std::cout << std::boolalpha << b.f(1, 2) << '\n';
Derived<int> d;
std::cout << std::boolalpha << d.f(std::make_pair(1, 2), std::make_pair(2, 2)) << '\n';
}
the compiler goes all agro on me:
file.cpp:9:41: error: no match for call to ‘(std::less<int>) (std::pair<int, int>&, std::pair<int, int>&)’
9 | virtual bool f(T t1, T t2) { return obj(t1, t2); }
| ~~~^~~~~~~~
...
/usr/include/c++/10/bits/stl_function.h:385:7: note: candidate: ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = int]’
385 | operator()(const _Tp& __x, const _Tp& __y) const
| ^~~~~~~~
/usr/include/c++/10/bits/stl_function.h:385:29: note: no known conversion for argument 1 from ‘std::pair<int, int>’ to ‘const int&’
385 | operator()(const _Tp& __x, const _Tp& __y) const
| ~~~~~~~~~~~^~~
So it's clear that the Base::f() is called instead of the redefinition. But why is this happening when in the first example everything was fine and the virtual functions behaved as intended?

virtual means that either Base::f or Derived::f is called depending on the dynamic type of the object.
The fact that you only call Derived<int>::f in main does not change that Base<std::pair<int,int>,std::less<int>::f is not valid.
A much simpler example with same issue is this:
#include <utility>
struct base {
virtual std::pair<int,int> foo(std::pair<int,int> x) {
return x+x;
}
};
struct derived : base {
std::pair<int,int> foo(std::pair<int,int> x) override {
return x;
}
};
int main() {
derived d;
}
Resulting in error:
<source>: In member function 'virtual std::pair<int, int> base::foo(std::pair<int, int>)':
<source>:6:17: error: no match for 'operator+' (operand types are 'std::pair<int, int>' and 'std::pair<int, int>')
6 | return x+x;
| ~^~
| | |
| | pair<[...],[...]>
| pair<[...],[...]>
Compiler returned: 1
Depending on what you are actually trying to achieve you might want to make Base::f pure virtual, or provide a specialization of Base when T is a std::pair, or inherit from Base<std::pair<T,T>,std::less<std::pair<T,T>>>.

Related

Overload resolution based on concept with dependent parameter

I am trying to use concepts to overload templated functions whose argument depends on the template parameter. Unfortunately this fails on gcc, reporting an ambiguous overload.
In my project I have code that compiles in gcc 9.3 and fails in gcc 12.2. But in trying to minimize the code, I've ended up with this, which fails on both gcc versions, but works in clang 15.0.0:
#include <type_traits>
#include <iostream>
struct A
{
using value_type = int;
};
struct B
{
using value_type = int;
};
template<typename Candidate>
concept something = requires {
typename Candidate::value_type;
};
template<typename Candidate>
concept something_specific = something<Candidate> && std::is_same_v<Candidate, A>;
template<something T>
void foo()
{
std::cout << "Something" << std::endl;
}
template<something_specific T>
void foo()
{
std::cout << "Something specific" << std::endl;
}
template<something T>
void bar(const typename T::value_type& s)
{
std::cout << "Something: " << s << std::endl;
}
template<something_specific T>
void bar(const typename T::value_type& s)
{
std::cout << "Something specific: " << s << std::endl;
}
int main()
{
foo<B>(); // works
foo<A>(); // works
bar<B>(1); // works
bar<A>(2); // ambiguous overload
return 0;
}
See godbolt, the error message is as expected:
<source>:48:11: error: call of overloaded 'bar<A>(int)' is ambiguous
48 | bar<A>(2); // ambiguous overload
| ~~~~~~^~~
<source>:32:6: note: candidate: 'void bar(const typename T::value_type&) [with T = A; typename T::value_type = int]'
32 | void bar(const typename T::value_type& s)
| ^~~
<source>:37:6: note: candidate: 'void bar(const typename T::value_type&) [with T = A; typename T::value_type = int]'
37 | void bar(const typename T::value_type& s)
| ^~~
I expected the something_specific overload to be chosen, because the concept that's restricting the template parameter is more specific. We can see this is true for foo already, otherwise it would be ambiguous as well.
Of course I can make it work with a requires clause, but I expected it to work as-is. So I'd like to understand whether this is just not supported or if it's a compiler issue. Is clang correct in compiling this code?

Error while trying to overload operator << for all std container printing, why?

I am trying to build an operator << overload to print all the standard library container types. So far, the overload works well, but when I use it in a generic program, it breaks when I try to print a simple std::string. In fact, this program:
#include <iostream>
#include <utility>
#include <vector>
#include <map>
#include <string>
// Helper Function to Print Test Containers (Vector and Map)
template <typename T, typename U>
inline std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& p) {
out << "[" << p.first << ", " << p.second << "]";
return out;
}
template <template <typename, typename...> class ContainerType, typename
ValueType, typename... Args>
std::ostream& operator <<(std::ostream& os, const ContainerType<ValueType, Args...>& c) {
for (const auto& v : c) {
os << v << ' ';
}
return os;
}
int main()
{
std::vector <int> v = { 1,2,3 };
std::cout << v;
std::map <int,int> m = { { 1, 1} , { 2, 2 }, { 3, 3 } };
std::cout << m;
std::string s = "Test";
std::cout << s;
}
Gives me this error:
prove.cpp: In function ‘int main()’:
prove.cpp:32:13: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’})
32 | std::cout << s;
| ~~~~~~~~~ ^~ ~
| | |
| | std::string {aka std::__cxx11::basic_string<char>}
| std::ostream {aka std::basic_ostream<char>}
prove.cpp:16:15: note: candidate: ‘std::ostream& operator<<(std::ostream&, const ContainerType<ValueType, Args ...>&) [with ContainerType = std::__cxx11::basic_string; ValueType = char; Args = {std::char_traits<char>, std::allocator<char>}; std::ostream = std::basic_ostream<char>]’
16 | std::ostream& operator <<(std::ostream& os, const ContainerType<ValueType, Args...>& c) {
| ^~~~~~~~
In file included from /usr/include/c++/9/string:55,
from /usr/include/c++/9/bits/locale_classes.h:40,
from /usr/include/c++/9/bits/ios_base.h:41,
from /usr/include/c++/9/ios:42,
from /usr/include/c++/9/ostream:38,
from /usr/include/c++/9/iostream:39,
from prove.cpp:1:
/usr/include/c++/9/bits/basic_string.h:6419:5: note: candidate: ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
6419 | operator<<(basic_ostream<_CharT, _Traits>& __os,
| ^~~~~~~~
I think that the problem is due to the fact that this overload is used to print also simple std::string objects, but I didn't find any suitable way to solve this so far. Any help? thanks.
Your operator<< overload may match types for which an operator<< overload is already defined. I suggest that you disable it for such types:
#include <type_traits>
template <template <typename, typename...> class ContainerType,
typename ValueType, typename... Args>
std::enable_if_t<!is_streamable_v<ContainerType<ValueType, Args...>>,
std::ostream&>
operator<<(std::ostream& os, const ContainerType<ValueType, Args...>& c) {
for (const auto& v : c) {
os << v << ' ';
}
return os;
}
The enable_if_t line uses SFINAE to disable the function for types that are already streamable.
The type trait is_streamable_v that is used above could look like this:
template<class T>
struct is_streamable {
static std::false_type test(...);
template<class U>
static auto test(const U& u) -> decltype(std::declval<std::ostream&>() << u,
std::true_type{});
static constexpr bool value = decltype(test(std::declval<T>()))::value;
};
// helper variable template:
template<class T>
inline constexpr bool is_streamable_v = is_streamable<T>::value;
Your original program should now work as expected:
int main() {
std::vector <int> v = { 1,2,3 };
std::cout << v;
std::map <int,int> m = { { 1, 1} , { 2, 2 }, { 3, 3 } };
std::cout << m;
std::string s = "Test";
std::cout << s;
}
Demo
The issue is that your << is too generic. It matches std::cout << some_string; and potentially other types for which there is already a << overload. The solution is to not provide overloads for types you do not own (unless it is explicitly permitted).
With minimal changes you can refactor your << to be a named function, then write a wrapper:
template <typename T>
struct my_print {
T& t;
};
template <typename T>
std::ostream& operator<<(std::ostream& out,const my_print<T>& mp) {
// call your named funtion template
do_the_printing(out,mp);
return out;
}
The caveat is that you need to explicitly create the instance:
std::cout << my_print(some_stl_container);
Of course you can also let your function template print directly and call it like this
do_the_print(std::cout,some_stl_container);

method, that calls same-named method of all base classes, if exists and saves return value to a list

I am writing a macro, which generates helper methods to call all same-named methods of all base classes of a current class, and skip the base classes, that do not have methods of that name.
(I am not posting the actual macro, because I don't want to hurt anybody, below is my test file with one macro-generated method)
I managed to have it working without preserving the return values of these method.
Now I want to save the values and return a list.
Below is a function, generated by my macro, it is supposed to call methods named "base_method" of all bases, with int and string as arguments.
I don't understand, why I am getting the error (below the code).
#include <type_traits>
#include <list>
#include <iostream>
namespace detail{
template <typename> struct sfinae_true : std::true_type{};
}
namespace detail{
template <typename T, typename A1, typename A2>
static auto test_base_method(int) ->
sfinae_true<decltype(std::declval<T>().base_method(std::declval<A1>(), std::declval<A2>()))>;
template <typename , typename A1, typename A2>
static auto test_base_method(long) ->
std::false_type;
template <typename T, typename A1, typename A2>
struct has_base_method : decltype(test_base_method<T, A1, A2>(0)){};
template <typename Base, typename T, std::enable_if_t<has_base_method<Base,int,std::string>::value, bool> = true >
auto call_base_method_if_any(T& obj, int arg1, std::string arg2) ->
decltype( obj.Base::base_method(std::declval<int>(), std::declval<std::string>()))
{
return obj.Base::base_method(arg1, arg2);
}
template <typename Base, typename T, std::enable_if_t<!has_base_method<Base,int,std::string>::value, bool> = false>
auto call_base_method_if_any(T&, int, std::string) -> bool
{
return false;
}
};
template <typename ... T>
class Z : public T ... {
public:
auto call_base_method_of_all_bases_if_any(int arg1, std::string arg2) -> std::list<bool> {
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
}
};
struct A{
bool base_method(int, bool){ std::cout << "A\n"; return true; }
bool base_method_narg(){ std::cout << "A no arg\n"; return true; }
};
struct B{ void base_method(int, bool){ std::cout << "B\n"; } };
struct C{ void base_method(int a, std::string b){ std::cout << "C, int = " << a << ", string = " << b; } };
struct D{ };
int main(){
Z<A> b;
Z<A,B> c;
Z<A,B,C> d;
Z<A,B,C,D> a;
std::cout << "a:" << std::endl;
auto x =a.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
std::cout << "b:" << std::endl;
b.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
std::cout << "c:" << std::endl;
c.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
std::cout << "d:" << std::endl;
d.call_base_method_of_all_bases_if_any(0, "string");
std::cout << std::endl;
}
Compiling errors:
g++ --std=c++14 expression_sfinae.3.cpp
expression_sfinae.3.cpp: In instantiation of ‘std::__cxx11::list<bool> Z<T>::call_base_method_of_all_bases_if_any(int, std::__cxx11::string) [with T = {A, B, C, D}; std::__cxx11::string = std::__cxx11::basic_string<char>]’:
expression_sfinae.3.cpp:48:63: required from here
expression_sfinae.3.cpp:27:99: error: no matching function for call to ‘std::__cxx11::list<bool>::list(<brace-enclosed initializer list>)’
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: candidate: template<class _InputIterator, class> std::__cxx11::list<_Tp, _Alloc>::list(_InputIterator, _InputIterator, const allocator_type&)
list(_InputIterator __first, _InputIterator __last,
^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: template argument deduction/substitution failed:
expression_sfinae.3.cpp:27:74: note: cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>]
list(initializer_list<value_type> __l,
^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate expects 2 arguments, 4 provided
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>]
list(list&& __x) noexcept
^
u/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: template argument deduction/substitution failed:
expression_sfinae.3.cpp:27:74: note: cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’
return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... };
^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>]
list(initializer_list<value_type> __l,
^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate expects 2 arguments, 4 provided
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>]
list(list&& __x) noexcept
^
// 9000 lines ommited
P.S. I also did not yet figured out how to deal with functions, that do not return anything (I don't think you can have a list of void). Any advices are welcome.
The root cause of the problem is that C's method returns void:
struct C {
void base_method(int a, std::string b);
^^^^^
};
And you're trying to use that return type to build up your std::list<bool>.
Going on the assumption that you'd want to exclude this case, I'd rewrite your details like so:
// this is the preferred overload (last argument is 'int' instead of '...')
// but will be SFINAE-d out if base_method() isn't callable with int or string
// or it returns something other than bool
template <class Base, class T,
class = std::enable_if_t<std::is_same<bool,
decltype(std::declval<Base&>().base_method(
std::declval<int>(),
std::declval<std::string>()))
>::value>>
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, int)
{
return obj.Base::base_method(arg1, arg2);
}
// fallback overload
template <class Base, class T>
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, ...) {
return false;
}
template <class Base, class T>
bool call_base_method_if_any(T& obj, int arg1, std::string arg2) {
return call_base_method_if_any_impl<Base>(obj, arg1, arg2, 0);
}

instantiation of nested template class

I have a class as shown below:
template <class T>
class outer {
public:
typedef T TType;
std::vector <TType> v;
int f1 (TType t) {cout << "t= " << t << endl;};
class inner {
public:
inner& operator=(const inner &in) {return *this;}
void f2(const inner &in) {cout << "in f2\n";}
};
inner read() {cout << "in read\n";return inner();}
};
Outer has to have nested inner. I have to create a Base class for outer (We are going backwards here !!). I should be able to derive outer1 from the Base. Existing clients of outer should work without changing anything. outer should just add code to derive from the base class.
My solution to this is:
template <typename T>
class outer_iface {
public:
typedef T TType;
std::vector <TType> v;
virtual int f1(TType t) {cout << "bt= " << t << endl;};
template <typename U>
class inner_iface {
public:
using value_type = U;
inner_iface& operator=(const inner_iface &in)
{
return static_cast <U*> (this)->operator=(in);
}
void f2(const inner_iface &in)
{
return static_cast <U*> (this)->f2(in);
}
}; //inner_biface
/*template <typename U>
typename inner_iface <U>::value_type read()
{
return static_cast <U*> (this)->read();
}*/
};
template <typename T>
class outer : public outer_iface <T> {
public:
typedef T TType;
std::vector <TType> v;
int f1 (TType t) {cout << "t= " << t << endl;};
class inner : public outer_iface <T> :: template inner_iface <inner> {
public:
inner& operator=(const inner &in) {return *this;}
void f2(const inner &in) {cout << "in f2\n";}
};
inner read() {cout << "in read\n";return inner();}
};
This compiles and builds. But, I have 2 questions:
is my declaration/definition of read correct in outer_iface?
how can I instantiate an outer_iface, with say int type, and call read ?
I tried from main():
outer_iface<int> oi;
oi.read();
clang gave errors:
g++ -g --std=c++11 test7.cpp
test7.cpp: In function ‘int main()’:
test7.cpp:62:11: error: no matching function for call to
‘outer_iface<int>::read()’oi.read();
^
test7.cpp:62:11: note: candidate is:
test7.cpp:28:40: note: template<class U> typename
outer_iface<T>::inner_iface<U>::value_type outer_iface<T>::read()
[with U = U; T = int]
typename inner_iface <U>::value_type read()
^
test7.cpp:28:40: note: template argument deduction/substitution failed:
test7.cpp:62:11: note: couldn't deduce template parameter ‘U’
oi.read();
So, obviously I don't have it right. How can I fix inner_face::read ?
Any help/insight is appreciated.
thanks
sdp
It seems that you want something like:
template <typename T>
class outer_iface {
public:
// ...
typename T::inner read()
{
// std::cout << "in read\n"; return typename T::inner();
return static_cast<T*>(this)->read();
}
};

Template function gets called instead of function of base type

I have a class hierarchy that can be written to an object using operator<<. The example looks as follows:
#include <iostream>
struct Base
{
};
struct Derived : public Base
{
};
struct Shift
{
template< typename U >
Shift& operator<<(const U&)
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Shift& operator<<(const Base&)
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
#if 0
Shift& operator<<(const Derived&)
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
#endif
};
int main()
{
Shift sh;
Base bas;
Derived der;
int u32 = 0;
sh << bas;
sh << der;
sh << u32;
}
This produces the following output:
Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const U&) [with U = Derived]
Shift& Shift::operator<<(const U&) [with U = int]
If I uncomment the #if 0 section it will change to the desired output:
Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const Derived&)
Shift& Shift::operator<<(const U&) [with U = int]
I have a lot of derived classes (actually a whole hierarchy) and until now I have to write a separate definition of operator<< for all those types. What I'd like is to have a solution where the operator for the base type is called for all types that are derived from Base. Is that possible?
P.S.: I tried several solutions, for example writing a helper class:
struct Base
{
};
struct Derived : public Base
{
};
template< typename T >
struct Helper
{
static void shift()
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
}
};
template< >
struct Helper< Base >
{
static void shift()
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
}
};
struct Shift
{
template< typename U >
Shift& operator<<(const U& value)
{
Helper< U >::shift();
return *this;
}
};
Output:
static void Helper<Base>::shift()
static void Helper<T>::shift() [with T = Derived]
static void Helper<T>::shift() [with T = int]
But still the base template is called, instead of the Base specialization.
P.P.S.: I'm currently limited to C++03 without Boost, unfortunately.
In your current implementation the templated overload is preferred as per §13.3.3.1.4 [over.ics.ref]/p1:
When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).
Since the derived-to-base Conversion is given a Conversion rank, to achieve the desired output the templated version with an identity conversion rank must be excluded from the set of viable functions during the overload resolution by using enable_if with a proper condition (SFINAE):
#include <type_traits>
//...
template <typename U>
auto operator<<(const U&)
-> typename std::enable_if<!std::is_base_of<Base, U>{}, Shift&>::type
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Shift& operator<<(const Base&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Output:
Shift& Shift::operator<<(const Base&)
Shift& Shift::operator<<(const Base&)
typename std::enable_if<(! std::is_base_of<Base, U>{}), Shift&>::type Shift::operator<<(const U&) [with U = int; typename std::enable_if<(! std::is_base_of<Base, U>{}), Shift&>::type = Shift&]
In c++03 the implementation is less readable:
template <bool B, typename T = void>
struct enable_if { typedef T type; };
template <typename T>
struct enable_if<false, T> {};
template <typename Base, typename Derived>
struct is_base_of
{
typedef char (&yes)[1];
typedef char (&no)[2];
static yes test(Base*);
static no test(...);
static const bool value = sizeof(test((Derived*)0)) == sizeof(yes);
};
template <typename Base, typename Derived>
const bool is_base_of<Base, Derived>::value;
//...
template <typename U>
typename enable_if<!is_base_of<Base, U>::value, Shift&>::type operator<<(const U&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
Shift& operator<<(const Base&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}