Overloaded function is not always selected before template instantiation - c++

I have created some code that uses a function template and an overload (not a specialization) of that function. When I call the function with an object of a class that is derived from the parameter in the it uses the template resulting in a compile error. I have been reading http://www.gotw.ca/publications/mill17.htm
and I had the impression that overloaded functions would always have preference above templates. I have created a similar do-nothing example:
class ITest
{
public:
virtual void foo()=0;
};
class TheTest:public ITest
{
public:
virtual void foo()
{
}
};
class test
{
public:
template<typename T>
void handle(T par)
{
par++;
}
void handle(ITest &t)
{
t.foo();
}
};
void TestThem()
{
test t;
t.handle(2);
t.handle(3.0);
TheTest t2;
t.handle(t2);
}
I would expect t.handle(t2) to call the overloaded void handle(ITest &t) since TheTest is derived from ITest. However the compiler selects the template which generates an error.
When I change void handle(ITest &t) to void handle(TheTest &t) it compiles fine.
I fixed it by removing the template function and overloading for all used types, but this is cumbersome since they all do exactly the same thing.

I had the impression that overloaded functions would always have preference above templates.
This is true, but only when the non template function and template function are equally good. In that case the non template function is used.
In this case though they are not both equally good. t2 is a TheTest, when overload resolution runs it finds void handle(ITest &t) and void handle(TheTest par) (I've instantiated the template here). Sine the template version will give an exact match it is a better function and is chosen.
The way to fix this is to constrain the template to only work for types that aren't derived from ITest. If you change the template function to
template<typename T, std::enable_if_t<!std::is_base_of_v<ITest, T>, bool> = true>
void handle(T par)
{
par++;
}
Then it will only be called for types that don't derive from ITest. You can see it working in this live example.

Related

How do I add a template specialization when for a generic method on a generic class when the two types are equal?

I'm trying to add in a specialization where the generic type of method and class agree, but I haven't been able to figure out exactly how to specify the template instantiation (if it is even possible).
My best guess would be something like the following (though it obviously doesn't compile):
template<typename ClassT>
class Foo
{
public:
ClassT x;
template<typename MethodT>
void Bar(MethodT arg)
{
}
};
template<typename T>
template<>
void Foo<T>::Bar(T arg)
{
x = arg;
}
As is usually the case when considering function template specialization, an overload can handle it:
template<typename MethodT>
void Bar(MethodT arg)
{
}
void Bar(ClassT arg)
{
x = arg;
}
When you call Bar, one of the candidates will be a function template specialization and one won't. Think of the class template as stamping out real, concrete member functions where possible when it's instantiated. There's a rule pretty late in overload resolution to prefer the one that isn't a function template specialization if it's a tie up to that point.
What you end up with is the second overload being called when there's an "exact match" in types (which allows for a difference in top-level const). If exact matches are too narrow, you can restrict the first overload to widen the second:
// Allow the other overload to win in cases like Foo<int>{}.Bar(0.0).
// std::enable_if works as well before C++20.
template<typename MethodT>
void Bar(MethodT arg) requires (not std::convertible_to<MethodT, ClassT>)
{
}
As discussed in the comments, it's not possible to do this with template specialization. However, something similar can be accomplished by using std::enable_if_t and
template<typename ClassT>
class Foo
{
public:
ClassT x;
template<typename MethodT,
typename = std::enable_if_t<!std::is_same<ClassT, MethodT>::value>>
void Bar(MethodT arg)
{
}
void Bar(ClassT arg)
{
x = arg;
}
};
std::enable_if_t will only return a valid type when the input type arg is true. Therefore, the template substitution will fail when MethodT and ClassT are the same type, but the non-template overload will not fail. The template substitution failure is ok under SFINAE.

Function template and regular overloads

The following code has a couple of regular function overloads, and - now - a template function intended as a catch-all if no overload is suitable.
It almost works as I want, except that using derived classes (which previously ended up in the regular overload) get handled by the function template instead.
#include <iostream>
class Base { };
class AnotherBase { };
class Derv : public Base{ };
class Derv2 : public Base { };
class DervDerv : public Derv { };
void f(const Base &b)
{
printf("b(Base)\n");
}
void f(const Derv &b)
{
printf("b(Derv)\n");
}
template<class T> void f(const T& t)
{
printf("b(template)\n");
}
int main() {
f(Base());
f(AnotherBase());
f(Derv());
f(Derv2());
f(DervDerv());
return 0;
}
So the output I get is this...
b(Base)
b(template)
b(Derv)
b(template)
b(template)
... when what I'd naively expected was this:
b(Base)
b(template)
b(Derv)
b(Base)
b(Derv)
Is a function overload of a base class really ranked as "lower quality" than a function template? If so, is there an easy way to change this?
https://ideone.com/jD2lgz
It's not about "quality". It's about conversions, just like it is with any other overload. To do overload resolution on the call to f(Derv2());, a compiler will synthesize a declaration like this one from your function template:
void f(const Derv2& t);
Which it pits against the other declared overloads. This overload gets picked for the exact same reason f(const Derv &) is a better match than f(const Base &) when you write f(Derv());.
A "catch-all" template will do just that, it will catch everything for which there isn't an exact user-defined overload. If you want to prevent that, you need to constrain the template with meta programming. A simple trick that relies on SFINAE would look like this:
template<class T>
auto f(const T& t) -> std::enable_if_t<!std::is_convertible<T*, Base*>::value>
{
printf("b(template)\n");
}
That produces the exact output you expected. Though obviously, you'd need to know what to constrain against in advance.

Using CRTP with same member function name

When I see the CRTP pattern used, it appears that the function name called in the base type always points to an implementation function of a different name in the derived type (ex: foo() in base makes the call static_cast<DerivedType*>(this)->foo_implementation();.
Is there a way to implement the CRTP pattern using the same function name? I have a longer inheritance chain in which the function might not have a concrete implementation in the first level of the chain, so having to use different function names is not very clean/readable.
I want to have something like the following:
template <typename SecondType>
struct FirstType {
void foo() {
static_cast<SecondType*>(this)->foo();
}
};
template <typename ThirdType>
struct SecondType : FirstType<SecondType> {
void foo() {
static_cast<ThirdType*>(this)->foo();
}
};
struct ThirdType : SecondType<ThirdType> {
void foo() {
// Concrete implementation here
}
};
Of course the compiler does not complain about this, but I would imagine that it results in an implicit vtable lookup (despite the virtual keyword not appearing), defeating the purpose of using the CRTP.
You can very well use the same name for both functions, it will work fine.
The advantage of using a different name is that failing to implement the function in the derived class will result in a compiler error, instead of an infinite recursion and probable stack overflow at runtime.
Why not assert that the member functions are different? Removing the foo() from ThirdType will give you a compile error with a nice message.
#include <type_traits>
template <typename SecondType>
struct FirstType {
void foo() {
static_assert(
!std::is_same<
decltype(&FirstType::foo),
decltype(&SecondType::foo)
>::value,
"SecondType must implement method 'void foo()'");
static_cast<SecondType*>(this)->foo();
}
};
template <typename ThirdType>
struct SecondType : FirstType<SecondType<ThirdType>> {
void foo() {
static_assert(
!std::is_same<
decltype(&SecondType::foo),
decltype(&ThirdType::foo)
>::value,
"ThirdType must implement method 'void foo()'");
static_cast<ThirdType*>(this)->foo();
}
};
struct ThirdType : SecondType<ThirdType> {
void foo() {
// Concrete implementation here
}
};
template class SecondType<ThirdType>;
template class FirstType<SecondType<ThirdType>>;

Variadic-templated class: support void without specializing the whole class?

I have a class:
template <typename ...Arguments>
class CSignalConnection
{
public:
CSignalConnection(std::function<void(Arguments...)> target) : m_target(target) {}
void invoke(Arguments&&... args) const
{
m_target(std::forward<Arguments>(args)...);
}
private:
std::function<void(Arguments...)> m_target;
};
I want to be able to declare CSignalConnection<void> and call invoke with no arguments. And I want to avoid specializing, i. e. duplicating the whole class, when I only have a couple void-incompatible methods (like invoke here which should be declared with 0 arguments). I have a couple more classes like this so I hate to write everything twice (and edit in two places).
One idea I have is to write both void and non-void invoke overloads and disable one with SFINAE, but I don't know how to actually implement this.
Using CSignalConnection<> will make the Arguments pack empty. The following works for me
void f() {
std::cout << "hello world\n";
}
int main () { {
CSignalConnection<> cs{&f};
cs.invoke();
}
However, since you have no type deduction this is a misuse of forwarding, you'll need to make invoke() deduce the types of its arguments to get real universal references (forwarding references)
template <typename ...Arguments>
class CSignalConnection {
public:
template <typename... Ts>
void invoke(Ts&&... args) const
{
m_target(std::forward<Ts>(args)...);
}
//... rest of the class omitted ...
};

how to call a function in C++11 templates only when its defined

Looking at a simple template scenario like this:
class A {
public:
int work();
};
class B {
public:
int work();
};
class ObjectManager {
public:
static void manage( A& obj );
// manage not defined for class B
};
template<class T>
doStuff( T t ) {
t.work();
....
ObjectManager::manage(t);
};
A a;
B b;
doStuf(a);
doStuff(b);
I am wondering, what is the cleanest way to implement a conditional call to ObjectManager::manage? The template function should determine in compile-time if ObjectManager::manage(T t) is defined for the given T and activate some lines of code only when it is. I guess there are solutions with some more nested template calls, but it would be the best for me just to keep the function in one piece.
Note that currently your class members are all private. They should be made public.
template<class T>
auto call_manage(T* t) -> decltype(ObjectManager::manage(*t)){
return ObjectManager::manage(*t);
}
void call_manage(...) {}
template<class T>
void doStuff( T t ) {
t.work();
//....
call_manage(std::addressof(t)); // smack whoever overloaded unary
// operator & in the head first
}
If the expression ObjectManager::manage(*t) is well-formed, substitution succeeds for the function template, which is always a better match than ..., so it is called. Otherwise, the do-nothing overload is the only viable function.
Demo.
For those who like references:
template<class T>
auto call_manage(T& t) -> decltype(ObjectManager::manage(t)){
return ObjectManager::manage(t);
}
template<class... T>
void call_manage(T&&...) {}
template<class T>
void doStuff( T t ) {
t.work();
//....
call_manage(t);
}
In this case, the single-argument overload is more specialized than the variadic version by the partial ordering rules for function templates, and is selected by overload resolution if both are equally viable. You can also make it take T&& t and call manage(std::forward<T>(t)), for perfect forwarding.
Demo.