Uses of a C++ Arithmetic Promotion Header

后端 未结 2 1453
没有蜡笔的小新
没有蜡笔的小新 2020-11-27 19:00

I\'ve been playing around with a set of templates for determining the correct promotion type given two primitive types in C++. The idea is that if you define a custom numer

2条回答
  •  温柔的废话
    2020-11-27 19:47

    For this, what you can use is the ?: operator. It will give you the common type between two types. First, if the two types are the same, you are fine. Then, if the types differ, you invoke the ?: and see what type you get back.

    You need to special case the non-promoted types char, short and their unsigned/signed versions thereof since applied to two of such operands of differing types, the result will be neither of them. You need also take care of the case where two classes can be converted to promoted arithmetic types. To get these right, we check whether the result of ?: is a promoted arithmetic type (in the spirit of clause 13.6), and use that type then.

    // typedef eiher to A or B, depending on what integer is passed
    template
    struct cond;
    
    #define CCASE(N, typed) \
      template \
      struct cond { \
        typedef typed type; \
      }
    
    CCASE(1, A); CCASE(2, B);
    CCASE(3, int); CCASE(4, unsigned int);
    CCASE(5, long); CCASE(6, unsigned long);
    CCASE(7, float); CCASE(8, double);
    CCASE(9, long double);
    
    #undef CCASE
    
    // for a better syntax...
    template struct identity { typedef T type; };
    
    // different type => figure out common type
    template
    struct promote {
    private:
      static A a;
      static B b;
    
      // in case A or B is a promoted arithmetic type, the template
      // will make it less preferred than the nontemplates below
      template
      static identity::type &check(A, T);
      template
      static identity::type &check(B, T);
    
      // "promoted arithmetic types"
      static identity::type &check(int, int);
      static identity::type &check(unsigned int, int);
      static identity::type &check(long, int);
      static identity::type &check(unsigned long, int);
      static identity::type &check(float, int);
      static identity::type &check(double, int);
      static identity::type &check(long double, int);
    
    public:
      typedef typename cond::type
        type;
    };
    
    // same type => finished
    template
    struct promote {
      typedef A type;
    };
    

    If your Complex types can be converted into each other, ?: won't find a common type. You could specialize promote to tell it how to figure out a common type of two Complex:

    template
    struct promote, Complex> {
      typedef Complex::type> type;
    };
    

    Usage is simple:

    int main() {
      promote::type a;
      int *p0 = &a;
    
      promote::type b;
      double *p1 = &b;
    
      promote::type c;
      string *p2 = &c;
    }
    

    Note that for real-world uses, you should best catch a few cases I left out for simplicity, for example should be handled similar to (you best first strip const and volatile and convert T[N] to T* and T& to T and afterwards delegate to the actual promote template - i.e do boost::remove_cv>::type for both A and B before delegating them). If you don't do this, the call to check will end up in an ambiguity for these cases.

提交回复
热议问题