Idiomatic C++11 type promotion

后端 未结 2 1826
无人共我
无人共我 2021-02-02 00:01

There is a great paper on C++ for scientific computing where the author (T. Veldhuizen) suggests a traits-based approach to address type promotion. I have used such app

相关标签:
2条回答
  • 2021-02-02 00:24

    As in catscradle answer, decltype and common_type (and custom specializations for it), are probably good C++11 replacement for the need of conversion traits that Veldhuizen have in mind. However, it will still fall short if you need to be still very specific for the evaluation of a function that maps two types into one (binary operator). (In other words decltype doesn't know about the mathematical domain of your problem).

    My take is that you can resort to Boost.MPL maps http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html, this doesn't even require C++11, it is just that MPL wasn't written at that time:

    #include<iostream>
    #include<complex>
    #include<typeinfo>
    #include <boost/mpl/map.hpp>
    #include <boost/mpl/at.hpp>
    // all traits in one place, no need for MACROS or C++11, compile error if the case does not exist.
    using namespace boost::mpl;
    typedef map<
        pair<pair<int, char>, int>,
        pair<pair<int, float>, int>,
        pair<pair<float, std::complex<float> >, std::complex<float> >
    > mapped_promotion;
    
    template<typename T1, typename T2>
    void product(T1 a, T2 b) {
      typedef typename at<mapped_promotion, pair<T1, T2> >::type T;
    
      T ans = T(a) * T(b);  
      std::cout<<"received "
               <<typeid(T1).name()<<"("<<a<<")"<<" * "
               <<typeid(T2).name()<<"("<<b<<")"<<" ==> "
               <<"returning "
               <<typeid(T).name()<<"("<<ans<<")"<<std::endl;
    }
    
    int main() {
      product(1, 'a');
      product(1, 2.0f);
      product(1.0f, std::complex<float>(1.0f, 2.0f));
      return 0;
    }
    

    Another extra benefit of using MPL is that you can then easily move to Boost.Fusion later, which usually is the case once you start dealing with "algebras" of types. And there is nothing to replace the functionality of Boost.Fusion in C++11 core language.


    What follows is a more general solution, you can stop reading if the above was enough for your application, that combines MPL and decltype and requires C++11 which allows for unspecified pair of types to default to the decltype solution if it is any good, the trick is to see if the return of the mpl::map is the metatype void_ (pair not found).

    ...
    #include <type_traits> 
    //specific promotions
    using namespace boost::mpl;
    typedef map<
        pair<pair<int, char>, int>,
        pair<pair<int, float>, int>,
        pair<pair<float, std::complex<float> >, std::complex<float> >
    > specific_mapped_promotion;
    
    //promotion for unspecified combinations defaults to decltype type deduction.
    template<class P1, class P2>
    struct loose_mapped_promotion : std::conditional<
        std::is_same<typename at<specific_mapped_promotion, pair<P1, P2> >::type, mpl_::void_>::value,
        decltype( std::declval<P1>()*std::declval<P2>() ),
        typename at<specific_mapped_promotion, pair<P1, P2> >::type
    > {};
    template<typename T1, typename T2>
    void product(T1 a, T2 b) {
      typedef typename loose_mapped_promotion<T1, T2>::type T;
    
      T ans = T(a) * T(b);
      ...
    }
    int main() {
       product(1.0, std::complex<double>(1.0f, 2.0f)); // now accepted, although no explicit trait was made
    }
    

    On a final note: it is apparently ok to overload std::common_type for special cases, if you want to use it: http://www.cplusplus.com/reference/type_traits/common_type/

    0 讨论(0)
  • 2021-02-02 00:25

    I think you can use decltype for this:

    template <typename T, typename U>
    void product(T t, U u) 
    {
        std::cout << typeid(decltype(t * u)).name() << std::endl;
    }
    

    Or with declval:

    #include <utility>
    template <typename T, typename U>
    void product() 
    {
        std::cout << typeid(decltype(std::declval<T>() * std::declval<U>())).name() << std::endl;
    }
    

    EDIT For T ans = T(a) * T(b); you can just use auto, auto ans = T(a) * T(b);

    0 讨论(0)
提交回复
热议问题