Conversion operator overloading - c++

I want to distinguish a template conversion operator between &, * and values:
struct S
{
template <class T>
constexpr operator T()
{
return value;
}
template <class T>
constexpr operator T&()
{
return value;
}
template <class T>
constexpr operator T*()
{
return &value;
}
int value;
} s{5};
int main()
{
uint32_t ui = s; // error: error C2440: 'initializing': cannot convert from 'S' to 'uint32_t
}
If I remove constexpr operator T&() the code compiles and ui = s invokes the constexpr operator T() operator. But why?
I also get strange behaviour when I add the explicit specifier to those funcitons.
It looks like the behaviour of the conversion operator differs from normal overloading. May anyone explain this?
PS: I'm using VS2017

Since value is of type int, it does not make sense to create a template conversion to a template parameter reference type. If the type is not int, you have a semantic error of trying to coerce an int object to a reference of some other type.
Redefine the reference conversion to the proper types:
constexpr operator int&()
{
return value;
}
constexpr operator const int&() const
{
return value;
}

Related

Why do I get "invalid use of incomplete type", when the class is fully defined?

I know this type of errors occur when a base class is only forward declared, but in my case it's fully implemented as far as I can tell:
I'm trying to create a unit system which would only compile if the correct units are used, employing literals and algebraic operators.
I start with a base class Units which is just a wrapper over T, and is inherited by all the other units.
Then I define the allowed algebraic operators, which should return the correct units.
I get
error: invalid use of incomplete type ‘class Units<T>’
[build] 107 | return Mps{static_cast<T>(rhs) / static_cast<T>(lhs)};
for this code:
template<typename T>
class Units
{
protected:
T val;
public:
constexpr explicit Units(T val) : val(val) { }
constexpr explicit operator T&() { return val; }
constexpr explicit operator T() const { return val; }
constexpr auto operator<=>(const Units<T> rhs) {
return val <=> rhs.val;
}
constexpr bool operator==(const Units<T> rhs) const { return val == rhs.val; }
};
template<typename T>
class Meters : public Units<T>
{
using typename Units<T>::Units;
};
template<typename T>
class Seconds : public Units<T>
{
using typename Units<T>::Units;
};
template<typename T>
class Mps : public Units<T>
{
using typename Units<T>::Units;
};
constexpr Meters<long double> operator "" _km(long double km) {
return Meters<long double>{1000 * km};
}
constexpr Seconds<long double> operator "" _s(long double s) {
return Seconds<long double>{s};
}
constexpr Mps<long double> operator "" _mps(long double s) {
return Mps<long double>{s};
}
template<typename T>
constexpr Mps<T> operator / (const Meters<T> &&rhs, const Seconds<T> &&lhs) {
return Mps{static_cast<T>(rhs) / static_cast<T>(lhs)};
}
int main() {
return 1_km / 2_s == 500_mps
}
Looks like the compiler is confused and the warning is misleading. Provide the missing template argument to fix it:
return Mps<T>{static_cast<T>(rhs) / static_cast<T>(lhs)};
^^^
You can (probably) define a deduction guide if you wish to avoid specifying the argument explicitly.
Other issues:
Missing semicolon.
Your operators are for floating point, but you use integer in 1_km; that won't work. Either use floating point literal, or add an overload for integers.
using typename Units<T>::Units; is wrong. Lose the typename.

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

clang rejects a template `/` operator but gnu c++ accepts it

In the context of unit management for scientific programming, I am managing the following class:
template <class UnitName>
class Quantity
{
double value;
public:
Quantity(double val = 0) : value(val) {}
Quantity(const Quantity &) {}
Quantity & operator = (const Quantity &) { return *this; }
double get_value() const noexcept { return value; }
operator double() const noexcept { return value; }
template <class SrcUnit>
Quantity(const Quantity<SrcUnit> &)
{
// here the conversion is done
}
template <class SrcUnit>
Quantity & operator = (const Quantity<SrcUnit> &)
{
// here the conversion is done
return *this;
}
template <class TgtUnit> operator TgtUnit() const
{
TgtUnit ret;
// here the conversion is done
return ret;
}
template <class U, class Ur>
Quantity<Ur> operator / (const Quantity<U> & rhs) const
{
return Quantity<Ur>(value / rhs.value);
}
};
Although the class is much more complex, I think I put enough information in order to describe my problem:
Now consider the following code snippet:
struct km_h {};
struct Km {};
struct Hour {};
Quantity<km_h> compute_speed(const Quantity<Km> & dist,
const Quantity<Hour> & time)
{
Quantity<km_h> v = dist/time;
return v;
}
This code is accepted by gnu c++ compiler and it runs well. The last template operator / is called.
But it is rejected by clang++ compiler (v 3.8.1) with the following message:
test-simple.cc:53:26: error: use of overloaded operator '/' is ambiguous (with operand
types 'const Quantity<Km>' and 'const Quantity<Hour>')
Quantity<km_h> v = dist/time;
~~~~^~~~~
test-simple.cc:53:26: note: built-in candidate operator/(__int128, unsigned long long)
test-simple.cc:53:26: note: built-in candidate operator/(unsigned long, long double)
So my questions would be: why clang++ rejects it? is a valid code? or gnu c++ should reject it?
In the case where the code would be valid, how could modify it in order to clang++ accept it?
I believe that clang is right to reject your code†, but gcc doesn't actually do what you want (both dist and time are implicitly convertible to double‡ and gcc believes the builtin operator/(double, double) is the best viable candidate). The problem is, you wrote:
template <class U, class Ur>
Quantity<Ur> operator / (const Quantity<U> & rhs) const
What is Ur? It's a non-deduced context - so attempting to invoke this operator as simply dist / time is a deduction failure. Your candidate is never considered. In order to actually use it, you'd have to explicitly provide Ur like so:
dist.operator/<Hour, km_h>(time); // explicitly providing Ur == km_h
Since that's awful, you can't have Ur be deduced as a template argument - you have to provide it yourself as some metafunction of the two units:
template <class U>
Quantity<some_mf_t<UnitName, U>> operator/(Quantity<U> const& ) const;
with some_mf_t is to be defined.
†You have both operator double() and template <class T> operator T(), which means that all the builtin operator/s are equally viable candidates (they're all non-template, exact matches).
‡Having operator double() sort of defeats the purpose of writing type safe units, no?

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.