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.
Related
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.
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.
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.
I have a base class MessageBase, from which I derive various other message classes, e.g., MessageDerived. I have another class that does some processing on various types of data, including a catchall method template:
struct Process {
void f(int a);
void f(const char* b);
template<typename T> void f(const T &t) { ... }
};
So if I call Process::f on a message object, the template method is called.
Now I want to add custom functionality for for my message classes. I am not allowed to change Process, so I want to derive from it (but even if I could change it, I can't find a way to get the custom functionality). I tried:
struct ProcessDerived : public Process {
void f(const MesaageBase& m) { ... } // Custom functionality for messages.
};
But that only works when I call ProcessDerived::f on a MessageBase object. If I invoke it on a MessageDerived object, the template method gets selected instead.
Is there a way to get the custom function selected on all message classes while letting the template catch all other types?
You need to use SFINAE here. Here is an example (note that it needs c++11 to work):
struct ProcessDerived : public Process
{
template<typename T> void f(const T &t, typename std::conditional<std::is_base_of<MessageBase, T>::value, int, void>::type = 0)
{
/// do something specific
}
template<typename T> void f(const T &t, ...)
{
return Process::f (t);
}
};
};
You can read more about it at http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
ProcessDerived::f shadows all definitions of Process::f (see here: Overloading rules for inheritance in C++). So calling ProcessDerived::f with a MessageDerived object calls ProcessDerived::f(const MessageBase&) as this is the only visible f, but e.g.
ProcessDerived pd;
pd(42);
won't compile. You need to add a
using Process::f;
in ProcessDerived for the problem you described showing up. The solution proposed by Alex Telishev while I was writing fixes both problems at once, however.
I have a class with a conversion operator to std::string. It works great with everything except with functions receiving std::basic_string<T> (templated on T).
#include <string>
struct A{
operator std::string(){return std::string();}
};
void F(const std::basic_string<char> &){}
template<typename T> void G(const std::basic_string<T> &) {}
int main(){
A a;
F(a); // Works!
G(a); // Error!
return 0; // because otherwise I'll get a lot of comments :)
}
The error I receive is
error: no matching function for call to 'G(A&)'
note: candidate is:
note: template<class T> void G(const std::basic_string<_CharT>&)
Now, I know I can define G as a friend in the struct A and it'll work, but my problem is with a lot of stl functions that already exist and receive std::basic_string<T> (for example, the operator<< printing function, or comparison operators, or many other functions.
I would really like to be able to use A as if it was an std::string. Is there any way to do this?
I would really like to be able to use A as if it was an std::string. Is there any way to do this?
Yes, but are you sure you really want this? The solution is:
struct A : public std::string {
};
but recall that std::string doesn't have a virtual destructor and therefore, cannot be used polymorphically. You have been warned!!!
A str() is a far better solution and allows you to be explicit when you want to pass your A to a function taking a std::basic_string<T>.
The compiler cannot infer that far; you'll either have to explicitly call the cast operator or to explictly specify the template parameter :
G(static_cast<std::string>(a));
G<char>(a);
To understand why the compiler can't do both user-defined conversion and template argument deduction, let's take this example :
template<typename T>
struct Number {
Number(double n) {};
Number(int n) {};
};
struct A{
operator Number<double>(){return Number<double>(1.);}
operator Number<int>(){return Number<int>(1);}
};
template<typename T> void G(Number<T>& number) { }
int main(){
A a;
G(a); // What do I do ?!
return 0;
}
What the compiler should do in that case ?
User defined conversions are not taken into consideration when performing template argument deduction.
Explicit specialization of G will work.
G<char>(a);