Chaining implicit conversion operators - c++

I've got a class which I need to implicitly convert to a few things, with intermediate values, e.g.
struct outer {
struct inner {
operator T() { return T(); }
};
operator inner() { return inner(); }
};
If I have this structure, is it always valid to do, e.g.
void f(T t);
outer o;
f(o);

§13.3.3.1.2 [over.ics.user] p1
A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (12.3) followed by a second standard conversion sequence.
Notice the singular and the missing of the word "sequence". Only one user-defined conversion will ever be considered during an implicit conversion sequence.

This works:
struct Foo {}; // renamed T in Foo to avoid confusion!
struct outer {
struct inner {
operator Foo() { return Foo(); }
};
operator inner() { return inner(); }
template <typename T>
operator T () {
return operator inner();
}
};
int main() {
void f(Foo t);
outer o;
f(o);
}
But only because f is not overloaded, so it is not really a solution.

Related

Why the user-defined conversion function template cannot have a deduced return type?

What is the reason of the following rule, "a user-defined conversion function template cannot have a deduced return type."
struct S {
operator auto() const { return 10; } // OK
template<class T> operator auto() const { return 42; } // error
};
Even if it was allowed, in the second line, there is nothing that depends on the template.
It can't be called (what is the purpose of T in that case ?)
If you want to convert to a user defined type, then you'll do that:
Let's say you have:
struct S
{
template<typename T> operator T() { return T(42); }
};
That's clear and there is no need to deduce anything.
You'd call this like this:
S s;
int v = s;
float f = s;
Please notice that, in that case, using auto instead of float in the code above would prevent the compiler to deduce the type (is it a float ? an int ? an Orange ?). The sentence above simply explains that.

Subscript operator and implicit conversion to a pointer type

I'm reading C++ Templates - The Complete Guide, 2nd Edition, and B.2.1 tells about implicit conversion of the implied "this" argument.
Same example here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1592.pdf
Depending on the typedef of ptrdiff_t, the compiler may deduce that there is an ambiguity between
BadString::operator[] and converting the implied "this" argument to char * and using the built-in subscript
operator.
Can somebody please explain how is obj[0] expression related to this conversion and why compiler acts the way it acts in three examples below?
Thank you.
int main() {
abc x;
auto first = x[1];
auto second = x + 2;
return 0;
}
Works (why?):
struct abc
{
operator bool *() { return {}; }
};
Doesn't work (why):
struct abc
{
template <typename T>
operator T *() = delete;
};
template <>
abc::operator int *() { return {}; }
Doesn't work (use of overloaded operator '[]' is ambiguous):
struct abc
{
operator bool *() { return {}; }
template <typename T>
operator T *() = delete;
};
template <>
abc::operator int *() { return {}; }

Overridden << operator not recognized

I'm trying to override the << operator but it seems that the compiler doesn't recognize my implementation and instead tries to interpret it as a bit shift.
I've already tried to play around with the parameter types (const T&, T&, T, const T) to no avail.
#pragma once
template<typename T> class AbstractStack
{
public:
virtual bool Push(const T &) = 0;
}
template <typename T> class ArrayStack : public AbstractStack <T>
{
public:
bool Push(const T&) {
....
}
}
template <typename T> bool operator<<(const AbstractStack<T>* &, const T&) {
return stack->Push(item);
}
int main() {
AbstractStack<int> *stack = new ArrayStack<int>(5);
int a = 2;
stack << a; // <<-- compiler error
return 0;
}
The error reported is:
Error (active) expression must have integral or unscoped enum type Lab10
Error C2296 '<<': illegal, left operand has type 'AbstractStack<int> *'
If I define the same operator acting on the class as a value, it just works...
When overloading operators, at least one of the arguments must be a class or an enum type - basically this allows/limits you to overloading custom types (user defined types).
From the cppreference;
When an operator appears in an expression, and at least one of its operands has a class type or an enumeration type, then overload resolution is used to determine the user-defined function to be called among all the functions whose signatures match the following...
This makes sense in that it disallows you from overloading the built in types; in this case, the pointer and integer you have as arguments.
As you already remarked in the question, the solution is taking your first argument by reference;
template <typename T>
bool operator<<(AbstractStack<T> &, const T&)
{ //...
Given the abstract base class you are looking to use, you could investigate the use of std::shared_ptr to help manage the resources and make the use of a "pointer" in the overloaded operator (albeit it will be a smart pointer);
template <typename T>
bool operator<<(std::shared_ptr<AbstractStack<T>>&, const T&)
{
return stack->Push(item);
}
int main() {
std::shared_ptr<AbstractStack<int>> stack = std::make_shared<ArrayStack<int>>(5);
int a = 2;
stack << a;
return 0;
}
As others have said, overloading any builtin operator requires an object of a user-defined type; a pointer won't work. And the solution is to use an object instead of a pointer:
template <typename T> bool operator<<(AbstractStack<T>&, const T&) {
return stack.Push(item);
}
and then call it with an object. There's no good reason in the code you've shown to allocate from the free-store; just create an auto object:
int main() {
ArrayStack<int> stack(5);
int a = 2;
stack << a;
return 0;
}

Conversion operators in class templates

I have two class templates TemplateA<T> and TemplateB<T>. Now, I want to define a conversion operator in TemplateB<T> in order to allow implicit type conversions from TemplateB<T> to TemplateA<T>. However, the following code produces a compilation error:
struct ClassA {};
template<typename T>
struct TemplateA {
T val;
};
template<typename T>
struct TemplateB {
T val;
template<typename ValT>
operator TemplateA() const {
TemplateA<ValT> a;
a.val = val;
return a;
}
};
int main() {
TemplateB<ClassA> b;
TemplateA<ClassA> a = b;
return 0;
}
Error:
main.cpp:13:12: error: expected type-specifier before 'TemplateA'
operator TemplateA() const {
^
I want to define a conversion operator in TemplateB<T> to allow implicit type conversions from TemplateB<T> to TemplateA<T>
That doesn't require a conversion function template. A plain conversion function will do:
operator TemplateA<T>() const {
TemplateA<T> a;
a.val = val;
return a;
}
You need a template only if you want to allow conversion from TemplateB<Foo> to TemplateA<Bar>.
In addition to T.C.'s answer, you could also define the conversion the other way if you so choose - instead of adding a [non-template] conversion function to TemplateB you can add a [non-template] converting constructor to TemplateA:
template<typename T>
struct TemplateB {
T val;
};
template<typename T>
struct TemplateA {
T val;
TemplateA() = default;
// converting constructor
TemplateA(TemplateB<T> const& t)
: val(t.val)
{ }
};
A template parameter forms part of the type definition, so you cannot omit it in your conversion operator:
template<typename ValT>
operator TemplateA<ValT>() const {
TemplateA<ValT> a;
a.val = val;
return a;
}
It helps to think that the compiler will append templated type to the computed type of a template. So your code will generate something like operator TemplateA_ClassA() const which will be used to convert TemplateB_ClassA to TemplateA_ClassA.

Why is a templated user-defined conversion operator able to determine its return type?

As stated in the title, why is this possible? Normally templated functions are unable to determine their return type if it's not among the input arguments and is not specifically stated.
For example:
class Foo {
public:
template<typename T>
operator T() { return T(); }
};
int main() {
Foo instance;
int someInteger = instance;
return 0;
}
Compiles and runs without any issues even though the return type is not explicitly stated anywhere. Is the user-defined conversion operator somehow special with regards to template rules?
I realize that it is syntactically not a return type. Nonetheless, it semantically is. After all, it is the type of the object that gets returned by the operator.
Edit:
The question should probably be "Why can't templated function determine their return type?"
This is invalid:
template <class T>
T sizeGetterFun()
{
return std::numeric_limits<T>::max();
}
int main() {
int maxInt = sizeGetterFun();
double maxDouble = sizeGetterFun();
return 0;
}
This is valid and achieves the same thing as requested from the invalid code.
class Foo {
public:
template<typename T>
operator T()
{
return std::numeric_limits<T>::max();
}
};
Foo sizeGetterFun()
{
return Foo();
}
int main() {
int maxInt = sizeGetterFun();
double maxDouble = sizeGetterFun();
return 0;
}
Why can't the compiler deduce the return type automatically without the need for a dummy class that implements the conversion operator? Are there any problems one can come across when using the second(working) example?
Because you declared a conversion function (template). A conversion function has no return type, and because it is a conversion function, it must return a T.
C++ standard section § 12.3.2 [conversion function] :
A member function of a class X having no parameters with a name of the
form
conversion-function-id:
operator conversion-type-id
conversion-type-id:
type-specifier-seq conversion-declarator opt
conversion-declarator:
ptr-operator conversion-declarator opt
specifies a conversion from X to the type specified by the
conversion-type-id. Such functions are called conversion functions. No return type can be specified.
Note:
If you try to explicit a return type, e.g. :
class Foo {
public:
template<typename T>
int operator T() { return T(); }
};
Then you'll get a compiler error (gcc gives "return type specified for 'operator T' ")