- Modern C++:Efficient and Scalable Application Development
- Richard Grimes Marius Bancila
- 468字
- 2021-06-10 18:28:16
Defining conversion operators
We have already seen that a constructor can be used to convert from another type to your custom type if your custom type has a constructor that takes the type you are converting. You can also perform the conversion in the other direction: converting the object into another type. To do this, you provide an operator without a return type with the name of the type to convert to. In this case, you need a space between the operator keyword and the name:
class mytype
{
int i;
public:
mytype(int i) : i(i) {}
explicit mytype(string s) : i(s.size()) {}
operator int () const { return i; }
};
This code can convert an int or a string to mytype; in the latter case, only through an explicit mention of the constructor.
The last line allows you to convert an object back to an int:
string s = "hello";
mytype t = mytype(s); // explicit conversion
int i = t; // implicit conversion
You can make such conversion operators explicit so that they will be called only when an explicit cast is used. In many cases, you will want to leave off this keyword because implicit conversions are useful when you want to wrap a resource in a class and use the destructor to do automatic resource management for you.
Another example of using a conversion operator is returning values from a stateful functor. The idea here is that the operator() will perform some action and the result is maintained by the functor. The issue is how do you obtain this state of the functor, especially when they are often created as temporary objects? A conversion operator can provide this functionality.
For example, when you calculate an average, you do it in two stages: the first stage is to accumulate the values and then the second stage is to calculate the average by dividing it by the number of items. The following functor class does this with the division performed as part of the conversion to a double:
class averager
{
double total;
int count;
public:
averager() : total(0), count(0) {}
void operator()(double d) { total += d; count += 1; }
operator double() const
{
return (count != 0) ? (total / count) :
numeric_limits<double>::signaling_NaN();
}
};
This can be called like this:
vector<double> vals { 100.0, 20.0, 30.0 };
double avg = for_each(vals.begin(), vals.end(), averager());
The for_each function calls the functor for every item in the vector, and the operator() simply sums the items passed to it and maintains a count. The interesting part is that after the for_each function has iterated over all of the items in the vector it returns the functor, and so there is an implicit conversion to a double, which calls the conversion operator that calculates the average.