简体   繁体   中英

Type aliases in C++

I have a class Point and a class Rect . I want to have two constructors for the Rect : One with a Point and a dimension (width, height) and one with two Points (top left, bottom right). Now it turns out that Point also can be seen as a dimension, so instead of creating a Dimension class I want to use my Point , basically like this:

class Point{...};
typedef Dimension Point;

class Rect{
    public:
        Rect(Point, Point);
        Rect(Point, Dimension);
}

So the question is: Does the compiler make a difference between Point and Dimension ? I tried it, the message ist "call of overloaded Rect(Point, Point) is ambiguous.".

How should I do that? Preferably without inheritance :)

EDIT

I understood now that it's the same to the compiler. But there is another scenario wher I need that.

I have a Point. The coordinates can be in a carthesian system (x, y) or GPS coordinates (lon, lat). It's perfectly ok for me to call the components x0 and x1 so I want to use only one class.

Now I want to calculate the distance between the two points and my idea is as follows:

typedef PointLonLat Point;
typedef PointXY Point;

double distance(PointLonLat, PointLonLat);
double distance(PointXY, PointXY);

PointLonLat p1(10, 10);
PointLonLat p2(11, 11);

double dist = distance(p1, p2); // the correct implementation is used

I know it doesn't work like that. But would the answer to that also be "make two classes"?

All typedefs are same for compiler.

You could do this instead:

//class template to be used to generate different class types!
template<int>
class Type{ /*...*/ };

//instantiate different class types, each with different template argument
typedef Type<0> Point;
typedef Type<1> Dimension;
typedef Type<2> Size;

class Rect{
    public:
        Rect(Point, Point);
        Rect(Point, Dimension); //its a different constructor!
};

Using this approach, you create different types out of the class template Type .


Instead of integer literals, you could use named enum as:

enum TypeArg
{
    PointArg,
    DimensionArg,
    SizeArg
};

template<TypeArg>
class Type{ /*...*/ };

typedef Type<PointArg>     Point;
typedef Type<DimensionArg> Dimension;
typedef Type<SizeArg>      Size;

Or even better use empty classes as:

//empty classes
struct PointArg{};
struct DimensionArg{};
struct SizeArg{};

template<typename T>
class Type{ /*...*/ };

typedef Type<PointArg>     Point;
typedef Type<DimensionArg> Dimension;
typedef Type<SizeArg>      Size;

This last approach is adopted by many C++ libraries, such as Boost.

You should make Dimension a different type. Yes, a Point can be used to specify a dimension, but it doesn't mean that it makes sense to use the same type.

The compiler makes no difference between Point and Dimension .

You have to create another class for Dimension . Just as a hint, you can use w and h instead of x and y for its members.

If point and dimension don't have identical behaviour, then your typedef is a logic error. If they do, then you don't need two constructors.


In response to your edit

For the example you've provided, what you have is two classes that store the same amount of data but have different behaviour. It's analagous to std::size_t and void* - they're both the same number of bits to the underlying hardware, but the language's type system gives them totally different meanings.

Your example could be solved by using two different classes, or by using a template class to avoid duplication like this:

enum CoordinateSystem {
  Geographic,
  Cartesian
};

template<CoordinateSystem C>
struct Point {
  double x,y;
};

double distance(Point<Cartesian>, Point<Cartesian>);
double distance(Point<Geographic>, Point<Geographic>);

一种快速的解决方案是,您可以有一个构造函数,并弄清楚逻辑内部提供的类

Although Dimension and Point are similar in that they can both be implemented as a pair of numbers, their behaviour isn't the same, eg Dimension would probably have member functions like height() and width() whereas Point might have x() and y(). So you shouldn't make them the same, but have different classes for each.

You can try making Dimension inherit from Point Instead of be a typedef:
class Dimension : public Point {}
Also, it is inefficient to pass the object types as copies; pass them as constant references instead. That has the additional benefit of allowing the compiler to generate polymorphic code for those types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM