conversion operator with template functions - c++

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);

Related

Overload operator [] with a template [duplicate]

In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.

Implicit conversion operator for templated types not automatically determined

I have a templated class thing with an implicit conversion operator, like follows:
#include <stdio.h>
template <typename T>
struct thing
{
T t;
operator const T&() const
{
return t;
}
};
template <typename T>
struct B
{
T t;
};
void fun(const int&) {
printf("int\n");
}
template <typename T>
void fun(const B<T>&) {
printf("B<T>\n");
}
int main()
{
thing<int> a;
fun(a);
thing<B<int>> b;
fun(b);
return 0;
}
Calling fun(const int&) with a thing<int>, the compiler is able to figure out to invoke the implicit conversion operator in order to pass the const T& (in this case const int&) to fun(const int&).
However, for a thing<B<int>>, the compiler can not figure out that I expect fun(const B<T>&) to be invoked.
How can I help the compiler figuring this out without casting b to const B<int>& explicitely (using static_cast<const B<int>&>(b) for instance)?
My concrete usage scenario is similar to the code provided with the constraints that I am using B with ~10 different types T, i.e. not arbitrary many different Ts. If I have to create ~10 template specializations, so be it. However, I don't exactly know how to best overload struct B in that case. But maybe I am on the wrong track - do simpler/more elegant solutions exist, maybe?
How can I help the compiler figuring this out without casting b to const B<int>&?
You can't. Templates do not do any implicit conversion. They deduce the type of the parameter and that is the type they use.
One thing you can do is add a get function to your wrapper like
template <typename T>
struct thing
{
T t;
operator const T&() const
{
return t;
}
const T& get() const
{
return t;
}
};
and then you can call fun like
fun(b.get());
Template argument deduction doesn't consider implicit conversion.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Then given fun(b);, the template fun can't be invoked because T can't be deduced.
You can specify the template argument explicitly, then overload resolution and implicit conversion would work fine.
fun<int>(b);
LIVE

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.

When is deleting a template instantiation preferable to deleting a non-template overload?

Suppose I have a template that works with raw pointers:
template<typename T>
void processPointer(T* ptr);
I don't want this to be called with void* pointers. It seems I have two choices. I can delete a non-template overload:
void processPointer(void*) = delete;
Or I can delete a template instantiation:
template<>
void processPointer<void>(void*) = delete;
Declaring the non-template overload is easier (no futzing with angle brackets). Are there reasons why I'd prefer to delete the template instantiation instead?
Here's one reason to favor the template version: processPointer<void>(void*) can still be invoked directly, avoiding the other overload.
I don't see any reason to go templating here
In fact, by deleting the non-template overload you may wiggle your way out of some edge-case ambiguous calls that I can't think of right now, since non-templates take precedence over template instantiations. And thus make this work as desired in a majority of cases.
This might give insight:
#include <iostream>
struct X
{
template<typename T>
void processPointer(T* ptr) {
std::cout << "Template\n";
}
// error: explicit specialization in non-namespace scope ‘struct X’
// template<>
// void processPointer(void*) = delete;
// Overload but no specialization
// This will prevent lookup the specialization outside the class, when no
// template argument is explicitly given. However, with an explicit
// template argument the specialization is called.
void processPointer(void*) = delete;
};
// Specialization outside the class body
template<>
void X::processPointer(void* ptr) {
std::cout << "Specialization\n";
}
int main ()
{
X x;
//error: use of deleted function ‘void X::processPointer(void*)’
//x.processPointer((void*)0);
// Explicit template argument:
x.processPointer<void>((void*)0);
}
Conclusion: The answer of #Casey holds.
Suppose you want to pass the argument pointer of type void* (or simply nullptr) to your processPointer function and you also want to call its specialization for type Type. Then you should write
processPointer(static_cast<Type>(pointer));
for
void processPointer(void*) = delete;
But for
template<>
void processPointer<void>(void*) = delete;
you could write the code which is much shorter:
processPointer<Type>(pointer);
So both variants can be used in the different cases.
However the analogue of the variant with non-template overload can be the only way in some cases.
Suppose there is a function template with two parameters:
template<typename T, typename U>
void processPointer(T* ptr1, U* ptr2);
You don't want it to be called with void* pointers as the first argument. The partial specialization of function templates is not allowed in C++ so this code is incorrect:
template<typename U>
void processPointer<void, U>(void*, U*) = delete;
And you must use another one:
template<typename U>
void processPointer(void*, U*) = delete;

Function template with an operator

In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.