C++ Templates: Byval/Reference interfering with eachother - c++

Here's a simplified version of my problem. I have a property class. It has data like has_initalized and such which i removed for this example.
When i call a function which uses T its fine. However T& isnt so i decided to write a T& version of it. But this causes all functions which uses plain T to get a compile error. Why is T& interfering with that? For this example how do i get both functions (Q and W) to work without changing main()?
template <class T>
class Property {
T v;
Property(Property&p) { }
public:
Property() {}
T operator=(T src) { v = src; return v; }
operator T() const { return v; }
operator T&() const{ return v; }
T operator->() { return v; }
};
class A{};
void Q(A s){}
void W(A& s){}
int main(){
Property<A> a;
Q(a);
W(a);
}

There is nothing in the overloading rules of C++ which allows the compiler to choose between operatorT() and operatorT&() in the call to Q. So removing the
operator T() const { return v; }
will also remove the ambiguity. But then you'll have a problem because returning a non const reference to a member in a const function is not possible.

For your Q, you can use both conversion functions. You can make the compiler prefer one over the other by making one non-const.
operator T() const { return v; }
operator T&() { return v; }
Now for Q, the operator T& is taken. This way will also fix the call to W to get a non-const reference. You can also return a const reference from the other
operator T const&() const { return v; }
operator T&() { return v; }
This way will still prefer the second conversion function for Q, but if your object a is const and you initialize a const reference, you won't always require to copy v.

Related

Overloading operator= for struct members of class std::vector<>

Say I have the following struct:
struct Parameter {
double value;
double error;
};
So that I'm usually working with vectors of that struct (ie. std::vector<Parameter>), and ocasionally I want to set a vector of values (but not errors) in that vector of parameters by using the operator= with a standard std::vector, for convenience.
std::vector<Parameter> vector_of_parameters;
std::vector<double> vector_of values;
....
vector_of_parameters = vector_of_values;
To do so, I'm trying to overload operator= for this struct as follows:
std::vector<Parameter> operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
But this will return an error saying that std::vector operator=(const std::vector& v) must be a non-static member. So if I understand it correctly, I have to define this as a member function of the operator as:
std::vector<Parameter>::operator=(const std::vector<double>& v) {
this->clear();
for (const auto& i:v) {
Parameter p;
p.value = i;
this->push_back(p);
}
return *this;
}
The error now says that a syntaxis with template<>, but I dont really see it, or understand it, and don't know what more can I do.
You cannot overload the assignment operator of std::vector. operator = must be a member function and you just can't add a member function to std::vector.
What you can do is make a convenience function like create_parameters that takes a std::vector<double> and returns a std::vector<Parameter>. That would look like
std::vector<Parameter> create_parameters(std::vector<double> const& params)
{
std::vector<Parameter> ret(params.size());
std::transform(params.begin(), params.end(), ret.begin(),
[](auto value) { return Parameter{value, 0}; });
return ret;
}
and then
vector_of_parameters = vector_of_values;
would become
vector_of_parameters = create_parameters(vector_of_values);
I think that an alternative simple way to create std::vector<Parameter> from std::vector<double> is defining a single argument constructor Parameter(double value) which accepts the Parameter::value:
#include <vector>
#include <optional>
struct Parameter
{
double value;
std::optional<double> error;
explicit Parameter(double v) : value(v)
{}
};
Then you can use range-constructor as follows:
DEMO
std::vector<Parameter> v_of_parameters(v_of_values.cbegin(), v_of_values.cend());

Understanding C++ choice of conversion operator

I've got an optional-like class (I can't use optional since it's in C++17). It contains a (possible) value along with a flag indicating if it's valid. I've got an explicit bool operator and a conversion operator to get the value out. The problem is, sometimes C++ will choose the bool operator in an explicitly bool context (an if statement), and other times it won't. Can anyone help me understand this behavior:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() {
return std::move(value());
}
explicit operator bool() const {
return valid_;
}
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1) {
double p = m1;
(void)p;
}
}
Whenever you write:
if (x)
That is equivalent to having written:
bool __flag(x);
if (__flag)
This is called a contextual conversion to bool (note that it's direct-initialization, so the explicit conversion function is a candidate).
When we do overload resolution on that construction for m0, there's only one valid candidate: explicit operator bool() const.
But when we do overload resolution on that construction for m1, there are two: explicit operator bool() const and operator double&&(), because double is convertible to bool. The latter is a better match because of the extra const qualification on the bool conversion function, even though we have to do an extra double conversion. Hence, it wins.
Would simply remove operator T&&() from your interface, as it doesn't make much sense for this type.
As soon as T is convertible to bool (double is, std::pair is not) your two operators will match and you'll get an ambiguous call, or one may be a better match for some reason.
You should only provide one of the two operators, not both.
Your operator bool and conversion operator are ambiguous in this design.
In the first context, std::pair<int,int> does not cast to bool
so the explicit bool conversion operator is used.
In the second context, double does cast to bool so the T
conversion operator is used, which returns a double, which then
casts to bool implicitly.
Note in the second context, you are calling std::move which puts value in a valid but undefined state, which leads to undefined behavior when you cast value to double a second time in the if block.
I'd use a named member function to indicate if it is valid, and modify the conversion operator:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() && { return std::move(value_); } // if rvalue
operator T&() & { return value_; } // if lvalue
operator const T&() const & { return value_; } // if const lvalue
bool valid() const { return valid_; }
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0.valid()) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1.valid()) {
double p = m1;
(void)p;
}
}
EDIT: The conversion operator should only move from member value_ if the maybe object is an rvalue reference. Using && after a functions signature specializes for this case -- please see Kerrek SB's answer for more information.

C++ identical method signature but different return type

I have seen the following code:
template <class T>
class Type {
public:
Type() {}
T& operator=(const T& rhs) {value() = rhs; return value();}
T& value() {return m_value;}
T value() const {return m_value;}
private:
T m_value;
};
Why does the compiler not complain about
T& value() {return m_value;}
T value() const {return m_value;}
and how to know which one is invoked?
The two functions are actually not the same. Only the second function is declared as a const member function. If the object that the member is called from is const, the latter option is used. If the object is non-const, the first option is used.
Example:
void any_func(const Type *t)
{
something = t->value(); //second `const` version used
}
void any_func2(Type *t)
{
something = t->value(); //first non-`const` version used
}
If both functions were declared non-const or both were declared const, the compiler would (should, anyway) complain.
Why does the compiler not complain about
Because the const counts for a different function signature. Your assumption the function signatures are identical is wrong.
The function marked as const will be invoked for any const instance or reference of Type<T>.
and how to know which one is invoked?
Put a cout statement in the functions and test the following cases:
template <class T>
class Type {
public:
Type() {}
T& operator=(const T& rhs) {value() = rhs; return value();}
T& value() {
std::cout << "non const version" << std endl;
return m_value;
}
T value() const {
std::cout << "const version" << std endl;
return m_value;
}
private:
T m_value;
};
int main() {
Type<int> t;
t.value();
Type<int> rt = t;
rt.value();
Type<int>* pt = &t;
pt->value();
const Type<int> ct;
ct.value();
const Type<int>& crt = t;
crt.value();
const Type<int>* pct = &t;
pct->value();
}
Your assignment operator will call the non const version.
The const version should better look like
const T& value() const {
std::cout << "const version" << std endl;
return m_value;
}
because you can't always rely on RVO (return value optimization), and extra copies might be taken (especially for older compiler implementations).
Also note the assignment operator should return a reference to the current instance:
Type& operator=(const T& rhs) {value() = rhs; return *this;}
A couple of words on functions resolution priority. Compiler distinguishes between const/non const functions on following way:
If a class has only const function with given name and argument list, it will be called for constant and non-constant objects alike. After calling this function, object will 'assume' constness (even if it was not const), meaning that the function can only call other const functions.
If a class has only non-const function, it will be called for non-const objects. Attempt to call this function for const objects will lead to compilation error.
If a class has both functions available, const version will be used for const objects, non-const version will be used for non-const objects.
Thanks to #owacoder for pointing my attention to initial mixup in the description.

std::equal_range not working with strucutre having operator< defined

I am trying to use std::equal_range with the structure below I have compilation error saying that error: no match for ‘operator<’ .
struct MyFoo {
int v_;
string n_;
bool operator<(int v) const
{ return v_ < v;}
};
vector<MyFoo> data;
// data is sorted by int v_
typedef vector<MyFoo>::iterator Ptr;
std::pair< Ptr, Ptr > pr = std::equal_range(data.begin(), data.end(), 10);
I've looked into the template implementatino and what is failing is the following where *it is deferenging the iterator pointing to an object of MyFoo and val_ is 10.
if(*it < val_) {
...
}
Why it is not working? I thought probably because it is trying to call the the global operator< that is not defined, but since I defined it as class member that should not be a problem, isn't it?
Provide non-member comparison operators :
bool operator<(int v, const MyFoo& foo)
{
return foo.v_ < v;
}
bool operator<(const MyFoo& foo, int v)
{
return v < foo;
}
Alternatively, you can provide a conversion operator to int :
operator int() cont {return v_;}
Which is probably unwanted, since the compiler will be able to perform silent conversions in other places of your code.
As an other alternative: provide
bool operator<(const MyFoo& rhs) const { return v_ < rhs.v_; }
And use std::equal_range on a dummy object with correct v_ as:
std::pair<Ptr, Ptr> pr = std::equal_range(data.begin(), data.end(), MyFoo{10, ""});
You may be having trouble because the std::equal_range implementation uses std::less. This is going to try to convert your MyFoo to an int to do the comparison, rather than just using an operator<() overload. Try adding this to your MyFoo class...
operator int() const
{
return v_;
}

C++ Templates and operator overloading

I am learning templates and operator overloading. I have written some code but I am confused... Let me explain...
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType():locked(false)
{
value = 0;
}
T operator=(T val)
{
value=val;
return value;
}
tempType<T> operator=(tempType<T> &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
And I did...
int main(void)
{
tempType<int> i;
tempType<bool> b;
tempType<float> f;
i.value = 10;
i = i + f;
return 0;
}
What code I need to write in order to execute
tempType<T> operator=(tempType<T> &val){}
Also, I why operator T() is required?
Unless you implement move semantics, operator= should always take a const & reference to the source value. It should also return a reference to the modified object.
tempType & operator=(T const & val)
{
value=val;
return * this;
}
operator T is an implicit conversion function which allows any tempType object to be treated as an object of its underlying type T. Be careful when specifying implicit conversions that they won't conflict with each other.
An implicit conversion function usually shouldn't make a copy, so you probably want
operator T & ()
{
return value;
}
operator T const & () const
{
return value;
}
Given these, you shouldn't need another overload of operator = because the first overload will simply be adapted by the conversion function to a call such as i = b;.
If a series of conversions will result in the operator=(T const & val) being called, you should avoid also defining operator=(tempType const & val) because the overloads will compete on the basis of which conversion sequence is "better," which can result in a brittle (finicky) interface that may refuse to do seemingly reasonable things.
I think I know all the answers so I might as well post full response.
To override default operator= you should declare it as tempType<T> operator=(const tempType<T> &val){}. Now you need to call the method explicitly via i.operator=(other_i).
If you correct the declaration you can use it like this:
tempType<int> i;
tempType<int> other_i;
i = other_i; // this is what you just defined
The operator T() is called a conversion operator. It is kind of reverse or counter part of the conversion constructor which in your case would be tempType(const &T value).
It is used to convert a class object into a given type. So in your case you would be able to write:
tempType<int> i;
int some_int;
some_int = i; // tempType<int> gets converted into int via `operator int()`
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType() : value(), locked(false)
{
value = 0;
}
//althought legal, returning something different from tempType&
//from an operator= is bad practice
T operator=(T val)
{
value=val;
return value;
}
tempType& operator=(const tempType &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
int main(void)
{
tempType<int> a;
tempType<int> b;
a = b;
return 0;
}
in the code, a = b calls the operator.
As for the second question, the operator T() is not needed "by default". In your example, it is used when you write i+f:
i is converted to an int
f is converted to a float
the operation (+) is performed
T tempType<int>::operator=(T val) is called for the assignement