Would it be possible to disable the Foo()
override in the derived class (by means of std::enable_if
or some boost magic), in case T is not of a certain type, without having to write a template specialization for class Derived
?
Bonus points: could the override be disabled if T does not define a certain method?
Here is my SSCCE:
#include <iostream>
#include <string>
class Base
{
public:
virtual std::string Foo()
{
return "Base";
}
};
template <typename T>
class Derived : public Base
{
public:
virtual std::string Foo() override
{
return "Derived";
}
};
int main()
{
Derived<int> testInt;
std::cout << testInt.Foo() << std::endl;
Derived<float> testFloat;
std::cout << testFloat.Foo() << std::endl;//I would like this to print 'Base'
}
UPDATE:
Thank you for the wonderful solutions, but I wasn't able to adapt them to my real code. The following example should provide a better idea of what I'm trying to achieve:
#include <iostream>
#include <string>
class Object
{
public:
void Test()
{
std::cout << "Test" << std::endl;
}
};
class EmptyObject
{
};
class Base
{
public:
virtual std::string Foo()
{
return "Base";
}
};
template <typename T>
class Derived : public Base
{
public:
virtual std::string Foo() override
{
m_object.Test();
return "Derived";
}
private:
T m_object;
};
int main()
{
Derived<Object> testObject;
std::cout << testObject.Foo() << std::endl;
Derived<EmptyObject> testEmpty;
std::cout << testEmpty.Foo() << std::endl;
}
I would do this by creating two functions that Derived::Foo
can delegate to conditionally based on whether T = float
. One would contain the real Derived::Foo
implementation, while the other would call Base::Foo
.
template <typename T>
class Derived : public Base
{
public:
virtual std::string Foo() override
{
return do_Foo(std::is_same<T, float>{});
}
private:
std::string do_Foo(std::false_type)
{
return "Derived";
}
std::string do_Foo(std::true_type)
{
return Base::Foo();
}
};
It seems what you actually want to do is call the Derived<T>::Foo()
implementation only if T
defines a certain member function, otherwise Base::Foo()
should be called. This can be done using expression SFINAE.
template <typename T>
class Derived : public Base
{
public:
std::string Foo() override
{
return do_Foo(true);
}
private:
template<typename U = T>
auto do_Foo(bool)
-> decltype(std::declval<U>().test(), void(), std::string())
{
return "Derived";
}
std::string do_Foo(int)
{
return Base::Foo();
}
};
In the code above, if the type T
does not define a member function named test()
, the do_Foo(bool)
member function template will not be viable. On the other hand, if T::test()
does exist, then do_Foo(bool)
will be selected because the boolean value being passed to do_Foo
by Foo
makes it a better match as compared to do_Foo(int)
.
A detailed explanation of what's going on within the decltype
expression in the trailing return type can be found here.