operator= Overload from a templated class - c++

I have a project will all my classes templated for int, double and float, getCoordinate return an object of the type CCoordinate.
tempCoordinate = m_shapes.at(i)->getCoordinate(j);
Before I apply the templates it was working correctly. But then some errors appear.
From what I understand I need I'm missing and operator= overload to typecast the values in case for example that i have a float and I'm receiving an int, for example:
CCoordinate<float> coorFloat;
CCoordinate<int> coorInt = coorFloat
How can i create this on my class? what format does it need ? .
I was thinking that it should look like this, but apparently i'm mistaken.
//CCoordinate.h
template<class T>
class CCoordinate {
//Code
public:
template<class U> template <class U> CCoordinate<T>
operator= (const CCoordinate<U>& c1);
}
//CCoordinate.cpp
template <class U >
CCoordinate<U> CCoordinate<T>::operator= (const CCoordinate<U>& c1)
{
// some kind of casting ?
}
My Errors:
19:06:43 **** Incremental Build of configuration Debug for project ShapesRefV2 ****
Info: Internal Builder is used for build
g++ -O0 -g3 -Wall -c -fmessage-length=0 -Werror=return-type -o "myCode\\CRectangle.o" "..\\myCode\\CRectangle.cpp"
g++ -O0 -g3 -Wall -c -fmessage-length=0 -Werror=return-type -o "myCode\\CPlane.o" "..\\myCode\\CPlane.cpp"
..\myCode\CPlane.cpp: In instantiation of 'GraSys::CRectangle<T> GraSys::CPlane<T>::boundingBox(std::string, std::string) [with T = int; std::string = std::basic_string<char>]':
..\myCode\CPlane.cpp:165:24: required from here
..\myCode\CPlane.cpp:115:20: error: no match for 'operator=' (operand types are 'GraSys::CCoordinate<double>' and 'const GraSys::CCoordinate<int>')
tempCoordinate = m_shapes.at(i)->getCoordinate(j);
^
..\myCode\CPlane.cpp:115:20: note: candidate is:
In file included from ..\myCode\CGraphicElement.h:14:0,
from ..\myCode\CPlane.h:11,
from ..\myCode\CPlane.cpp:9:
..\myCode\CCoordinate.h:17:7: note: GraSys::CCoordinate<double>& GraSys::CCoordinate<double>::operator=(const GraSys::CCoordinate<double>&)
class CCoordinate
^
..\myCode\CCoordinate.h:17:7: note: no known conversion for argument 1 from 'const GraSys::CCoordinate<int>' to 'const GraSys::CCoordinate<double>&'
..\myCode\CPlane.cpp: In instantiation of 'GraSys::CRectangle<T> GraSys::CPlane<T>::boundingBox(std::string, std::string) [with T = float; std::string = std::basic_string<char>]':
..\myCode\CPlane.cpp:166:24: required from here
..\myCode\CPlane.cpp:115:20: error: no match for 'operator=' (operand types are 'GraSys::CCoordinate<double>' and 'const GraSys::CCoordinate<float>')
tempCoordinate = m_shapes.at(i)->getCoordinate(j);
^
..\myCode\CPlane.cpp:115:20: note: candidate is:
In file included from ..\myCode\CGraphicElement.h:14:0,
from ..\myCode\CPlane.h:11,
from ..\myCode\CPlane.cpp:9:
..\myCode\CCoordinate.h:17:7: note: GraSys::CCoordinate<double>& GraSys::CCoordinate<double>::operator=(const GraSys::CCoordinate<double>&)
class CCoordinate
^
..\myCode\CCoordinate.h:17:7: note: no known conversion for argument 1 from 'const GraSys::CCoordinate<float>' to 'const GraSys::CCoordinate<double>&'
19:06:44 Build Finished (took 674ms)

In the member declaration you have template <class U> too many times, and the member should return a reference to *this, so it needs to return CCordinate & (the <T> is implied if you omit it):
// Remove this vvvvvvvvvvvvvvvvvv
template<class U> /* template <class U> */
CCoordinate & operator= (const CCoordinate<U>& c1);
// ^- Return type changed to be a reference.
Since the member is a template and the class is a template, you have two levels of templates. You need to specify both levels when you implement the member.
It is also returning the wrong type (it returns CCoordinate<U> but you have declared it to return CCoordinate<T> in the class).
// You need the T template as well.
// vvvvvvvvvvvvvvv
template <class T>
template <class U>
CCoordinate<T> & CCoordinate<T>::operator= (const CCoordinate<U>& c1)
// ^ ^- Added reference as per above.
// \---- Changed to T; U makes no sense here and conflicts with your member
// declaration in the class.
{
// Your logic to make the conversion.
return *this;
}

There are two issues with your attempt. The simpler issue is that the declaration syntax of your operator= has an extra template <class U>. It should look like this:
template<class U> CCoordinate<T>
operator= (const CCoordinate<U>& c1);
However, even a correctly defined operator= will not allow you to write
CCoordinate<float> coorFloat;
CCoordinate<int> coorInt = coorFloat;
This is because the second line above copy initializes coorInt. operator= is not considered for copy initialization - it only looks at user-defined conversions which only include non-explicit constructors and non-explicit conversion functions in this case.

Related

Assignment operator overloading with a C++ class template

I have a C++ class template for representing real- and complex valued 2D fields. I'd like to overload the assignment operator to achieve deep-copying the data from one field to another. For now, I've restricted the data to either double or std::complex<double>. This means there are 4 different cases to consider: double-to-double, double-to-std::complex<double>, std::complex<double>-to-double and std::complex<double>-to-std::complex<double>. I want to handle the std::complex<double>-to-double case by taking the real part of the complex value; for the other cases it's just a trivial assignment. I'm however struggling to get the code to compile. The following is a simple mock version that captures the issues:
#include <complex>
template<class T>
class Number {
private:
// should make Number<T>.m_value visible to Number<U>
template<class U>
friend class Number;
T m_value;
public:
Number(const T value) : m_value{value} {
// restricting the data
static_assert(
std::is_same<T, double>::value || std::is_same<T, std::complex<double>>::value,
"Error: Number::Number: Only 'double' and 'std::complex<double>' are supported currently!"
);
// no copying allowed
Number(const Number& orig) = delete;
};
// general case
template<class U>
Number<T>& operator=(const Number<U>& another) {
m_value = another.m_value;
return *this;
}
};
// specialization
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
m_value = std::real(another.m_value);
}
int main(int argc, char** argv) {
const std::complex<double> I{0.0, 1.0};
Number<double> n{0.0};
Number<std::complex<double>> m{1.0 + I*2.0};
n = m;
return 0;
}
Compiler output:
g++ -c -g -std=c++14 -MMD -MP -MF "build/Debug/GNU-Linux/main.o.d" -o build/Debug/GNU-Linux/main.o main.cpp
main.cpp:28:28: error: template-id ‘operator=<>’ for ‘Number<double>& Number<double>::operator=(const Number<std::complex<double> >&)’ does not match any template declaration
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
^
main.cpp:28:97: note: saw 1 ‘template<>’, need 2 for specializing a member function template
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
^
main.cpp: In instantiation of ‘Number<T>& Number<T>::operator=(const Number<U>&) [with U = std::complex<double>; T = double]’:
main.cpp:36:5: required from here
main.cpp:23:15: error: cannot convert ‘const std::complex<double>’ to ‘double’ in assignment
m_value = another.m_value;
^
I can't seem to figure out how to implement the assignment overloads. I have tried to find a solution for my problem (e.g. from "Similar questions") and have come across many helpful questions and answers, and have incorporated many things from them to my code. I however haven't found a solution to my specific problem and seem to be stuck. Any suggestions? Thanks!
It actually says what it expects in the line:
main.cpp:28:97: note: saw 1 ‘template<>’, need 2 for specializing a member function template
You need one 'template<>' to specialize T in class template and one more to specialize U in member function template:
template<>
template<>
Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) { ... }

Automatic conversion from derived class to base class' member variable's type

Long story short: I'd like to understand why the D::operator B() const conversion operator is not used in the last line in the code below, which thus fails when compiling with g++ -std=c++17 source.cpp (compiling with g++ -std=c++2a deleteme.cpp is successful, though).
The error is:
$ g++ -std=c++17 deleteme.cpp && ./a.out
In file included from /usr/include/c++/10.2.0/cassert:44,
from deleteme.cpp:1:
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:19:14: error: no match for ‘operator==’ (operand types are ‘D’ and ‘B’)
19 | assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
| ~ ^~ ~~~~
| | |
| D B
In file included from /usr/include/c++/10.2.0/utility:70,
from deleteme.cpp:2:
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5: note: candidate: ‘template<class _T1, class _T2> constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)’
466 | operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
| ^~~~~~~~
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10.2.0/cassert:44,
from deleteme.cpp:1:
deleteme.cpp:19:20: note: ‘B’ is not derived from ‘const std::pair<_T1, _T2>’
19 | assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
|
The code is:
#include <cassert>
#include <utility>
struct B {
int x;
B(int x) : x(x) {}
bool operator==(B const& other) const { return x == other.x; }
};
struct D : std::pair<B,char*> {
operator B() const { return this->first; }
};
int main() {
B b{1};
D d{std::pair<B,char*>(B{2},(char*)"hello")};
assert((B)d == B{2}); // conversion operator invoked explicitly is fine
assert(d == B{2}); // conversion operator not invoked explicitly errors // LINE
}
This question is a follow up to this. There I got help to write a class Recursive which behaves like a pair (so inherits from it) whose first is a std::array and whose second is a boost::hana::optional<std::vector<...>> (see the link for details).
Since the second of the std::pair is kind of an "advanced" information to what's contained in the first, in many places I'd like to cast/convert this object of std::pair-like class Recursive to the type of its first.
When compiler sees d == B{2}, it first creates a list of operator== overloads that it's able to find, and then performs overload resolution on them.
As the link explains, the overload list contains:
Member operator==s of the first operand, if any.
Non-member operators==s found by unqualified lookup, if any.
Built-in operator==s, if your operands can be converted to built-in types.
There's no mention of examining conversion operators of the first operand, so your operator== doesn't get found.
The solution is to make the operator== non-member (possibly define it as a friend). This works:
friend bool operator==(const B &a, const B &b) {return a.x == b.x;}
Starting from C++20, the rules of comparison operators got relaxed: the compiler will look for member operator== in the second operand as well, and will happily call non-member operator== with arguments in a wrong order.
So a == b and b == a are now equivalent to a degree, except that:
An operator== called in this new manner must return bool.
If given choice, the compiler will prefer the old ways of calling operator== over calling the member operator== of the rhs, which is in turn preferred over calling a non-member operator== with a wrong argument order.

C++ Templates: correct way to return a new type

Sorry for the generic title, but I'm unable to focus the problem.
I have a templatized class method that accept an argument pack and provides a new type in return, to hide the details of the implementation. More specifically, the class handles SQLite queries, and the method calls sqlite3_prepare() to prepare the statement before executing the query.
class Table {
...
template <typename ...Ts>
class PreparedStatement { ... };
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( ... );
}
That works well with "normal" types, but the problem occurs when the arguments are declared const:
const Field<int> fld = createField<int>("name");
...
PreparedStatement<decltype(fld)> s = prepare(make_tuple(fld));
The error is the following:
no match for 'operator =' (operand types are PreparedStatenent<const Field<int>> and PreparedStatement<Field<int>>
I suspect the issue is in my declaration of the function, is there a way to fix this issue and make the function more "elegant" ?
NOTE: I know I can fix the issue by manually declare the s variable, but my doubts are on how the method was implemented.
As Many Asked, here's an example:
#include <tuple>
template <typename T>
struct Field {
};
class Table {
public:
template <typename ...Ts>
class PreparedStatement {
public:
PreparedStatement() {};
};
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( );
}
};
int main()
{
Field<int> fld;
Table t;
Table::PreparedStatement<decltype(fld)> p;
p = t.prepare(std::make_tuple(fld));
// here comes the problem
const Field<int> f2;
Table::PreparedStatement<decltype(f2)> p2;
p2 = t.prepare(std::make_tuple(f2));
return 0;
}
and here's the compiler output
main.cpp: In function 'int main()': main.cpp:35:39: error: no match
for 'operator=' (operand types are 'Table::PreparedStatement >' and 'Table::PreparedStatement >')
p2 = t.prepare(std::make_tuple(f2));
^ main.cpp:10:10: note: candidate: constexpr Table::PreparedStatement >&
Table::PreparedStatement >::operator=(const
Table::PreparedStatement >&)
class PreparedStatement {
^~~~~~~~~~~~~~~~~ main.cpp:10:10: note: no known conversion for argument 1 from 'Table::PreparedStatement >'
to 'const Table::PreparedStatement >&'
main.cpp:10:10: note: candidate: constexpr
Table::PreparedStatement >&
Table::PreparedStatement
::operator=(Table::PreparedStatement >&&) main.cpp:10:10: note: no known conversion for argument 1 from
'Table::PreparedStatement >' to
'Table::PreparedStatement >&&'
UPDATE
As many noted, I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context.
So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
cppreference.com for make_tuple tells us:
template< class... Types >
tuple<VTypes...> make_tuple( Types&&... args );
For each Ti in Types..., the corresponding type Vi in Vtypes... is
std::decay<Ti>::type unless application of std::decay results in
std::reference_wrapper<X> for some type X, in which case the deduced
type is X&.
While std::decay, among other things, removes cv-qualifiers. So your type will be no PreparedStatement<const Field<int>>, but PreparedStatement<Field<int>>.
You can use auto, as manni66 proposed, to avoid such problems.
auto s = prepare(make_tuple(fld));
I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context. So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
Instead of auto, you can use a decltype expression that take in count the value returned by prepare.
I mean... instead of
Table::PreparedStatement<decltype(f2)> p2;
you can try with
decltype(t.prepare(std::make_tuple(f2))) p2;
or
decltype(std::declval<Table>().prepare(
std::make_tuple(std::declval<Field<int>>()))) p2;
I suppose you can use a similar decltype() also to declare members of your classes.

How do I implement "perfect forwarding" in a class template?

I have a class template with template parameter T and a member of type T. I want to initialize that member with a parameter passed to the ctor and I also want the passed parameter to be moved if it's a rvalue reference and if T supports move semantic:
template <typename T>
class C {
public:
explicit C(T t) : t_(t)
{
}
explicit C(T&& t) : t_(std::move(t))
{
}
...
private:
T t_;
};
g++ 4.8 gives the following error if I try to pass rvalue reference to the ctor:
int main()
{
int x = 0;
C<int> p1{x}; // OK
C<int> p2{1}; // error g++-4.8: call of overloaded ‘C(<brace-enclosed initializer list>)’ is ambiguous
return 0;
}
The full error text:
g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
main.cpp: In function ‘int main()’:
main.cpp:23:16: error: call of overloaded ‘C()’ is ambiguous
C p2{1}; // error g++-4.8: call of overloaded ‘C()’ is ambiguous
^
main.cpp:23:16: note: candidates are:
main.cpp:12:11: note: C::C(T&&) [with T = int]
explicit C(T&& t) : t_(std::move(t))
^
main.cpp:8:14: note: C::C(T) [with T = int]
explicit C(T t) : t_(t)
^
main.cpp:6:7: note: constexpr C::C(const C&)
class C {
^
main.cpp:6:7: note: constexpr C::C(C&&)
Could someone help me, please?
Thanks!
I solved the problem by making the parameter t in C(T t) const reference.

How does make_pair know the types of its args?

The definition for make_pair in the MSVC++ "utility" header is:
template<class _Ty1,
class _Ty2> inline
pair<_Ty1, _Ty2> make_pair(_Ty1 _Val1, _Ty2 _Val2)
{ // return pair composed from arguments
return (pair<_Ty1, _Ty2>(_Val1, _Val2));
}
I use make_pair all the time though without putting the argument types in angle brackets:
map<string,int> theMap ;
theMap.insert( make_pair( "string", 5 ) ) ;
Shouldn't I have to tell make_pair that the first argument is std::string and not char* ?
How does it know?
Function template calls can usually avoid explicit template arguments (ie make_pair<…>) by argument deduction, which is defined by C++03 §14.8.2. Excerpt:
When a function template
specialization is referenced, all of
the template arguments must have
values. The values can be either
explicitly specified or, in some
cases, deduced from the use.
The specific rules are a bit complicated, but typically it "just works" as long as you have only one specialization which is generally qualified enough.
Your example uses two steps of deduction and one implicit conversion.
make_pair returns a pair<char const*, int>,
then template<class U, classV> pair<string,int>::pair( pair<U,V> const & ) kicks in with U = char*, V = int and performs member-wise initialization,
invoking string::string(char*).
It doesn't. make_pair generated a pair<char*,int> (or maybe a pair<char const*,int>).
However, if you'll note in the implementation of pair there's a templated copy constructor:
template < typename Other1, typename Other2 >
pair(pair<Other1,Other2>& other)
: first(other.first), second(other.second)
{}
This may be implemented in slightly different ways but amounts to the same thing. Since this constructor is implicit, the compiler attempts to create pair<std::string,int> out of your pair<char*,int> - since the necessary types are convertible this works.
make_pair() exists precisely so that argument type deduction can be used to determine the template parameter types.
See this SO question: Using free function as pseudo-constructors to exploit template parameter deduction
It relies on the fact that the constructor of std::string accepts a const char*.
It doesn't matter if this constructor of std::string is explicit or not. The template deducts the type and uses the copy constructor of pair to convert it. It also doesn't matter whether or not the pair constructor is explicit.
If you turn the constructor of std::string into:
class string
{
public:
string(char* s)
{
}
};
you get this error:
/usr/include/c++/4.3/bits/stl_pair.h: In constructor ‘std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U1 = const char*, _U2 = int, _T1 = const string, _T2 = int]’:
foo.cpp:27: instantiated from here
/usr/include/c++/4.3/bits/stl_pair.h:106: error: invalid conversion from ‘const char* const’ to ‘char*’
/usr/include/c++/4.3/bits/stl_pair.h:106: error: initializing argument 1 of ‘string::string(char*)’
The constructor looks like this:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first),
second(__p.second) { }
The copy constructor looks like this:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first),
second(__p.second) { }