Template for return-type



  • Hello again!

    I have a great problem, which can be demonstrated in a simple code.

    Imagine, that I want to write Sqr(x) function, which should return x*x for every type of argument x having operator* :

    template<class T> inline T Sqr(T const &x)
    {
      return x*x;
    }
    

    But that function does not work for every type of x . It works only for those types, which produces the same type by multiplication. I cannot use this function, for example, for vectors, where vector*vector==scalar (“scalar product”).

    I want to have something like this:

    template<class T> inline ReturnTypeOf(T::operator*(T)) Sqr(T const &x)
    {
      return x*x;
    }
    

    or this:

    template<class T> inline [Dear compiler! See type of my return operator! I have only one such operator.] Sqr(T const &x)
    {
      return x*x;
    }
    

    P.S.: #define works for this case, but it does not work, for example, for overloaded operators.



  • template<class T, class TR> inline TR Sqr(T const &x)
    {
      return x*x;
    }
    

    also does not work 😞 . Compiler error “Cannot deduce template parameter for TR” when trying to call such a function.


  • Mod

    Currently, there is no general solution for this problem. Here we need to determine the type from an expression without evaluating that expression (in order to use it in a declaration), however, some compilers do have extensions to make that possible (__typeof). If the set of possible types is known, a solution exists using some nifty template techniques in conjunction with sizeof. In that case, boost's typeof library is viable:

    // helper-template, no definition required
    template<typename T> const T& make();
    
    // BOOST_TYPEOF_TPL( T() * T() )  cannot handle types that are not default-constructible
    template<typename T> BOOST_TYPEOF_TPL( make<T>() * make<T>() ) sqr(const T& x) { return x * x; }
    

    In the upcoming standard, this problem shall have an elegant solution:

    template<typename T> constexpr sqr(T&& x) -> decltype( x * x ) { return x * x; }
    


  • you could provide a helper template

    teplate <typename T> 
    struct SquaredType {
      typedef T ResultType; //normally T*T gives another T
    };
    
    template<>
    struct SquaredType<3DVector> {
      typedef Scalar ResultType;  //Product of 2 3DVector gives a Scalar
    };
    
    template <typename T>
    SquaredType<T>::ResultType Sqr(T const& x)
      {return x*x;}
    

    now you only have to specialize SquaredType for types where the multiplication gives another type



  • Thanks. How to use __typeof technique?

    I thinked for several days and thinked out the following solution:

    class cVector
    {
      ...
    public:
      template<class C> struct tMult{};
      template<> struct tMult<cVector>{typedef double type;}; //The type of vector*vector
      template<> struct tMult<double>{typedef cVector type;}; //The type of vector*double 
    
      ...
    };
    

    Now, for all classes which have tMult struct I can do the following:

    template<class A, class B> inline typename A::tMult<B>::type Multiply(A const &a, B const &b)
    {
      return a*b;
    }
    

    P.S.: Why “typedef template is illegal” ?? Why I need to store type in template struct?



  • SAn schrieb:

    TP.S.: Why “typedef template is illegal” ?? Why I need to store type in template struct?

    Good question. It just is. Again, the next standard will have a solution to this problem (albeit not using the keyword 'typedef' but rather a templated 'using'). At the moment the best way to work around this restriction is by using meta functions – those 'template struct' constructs.




  • Mod

    Konrad Rudolph schrieb:

    SAn schrieb:

    TP.S.: Why “typedef template is illegal” ?? Why I need to store type in template struct?

    Good question. It just is. Again, the next standard will have a solution to this problem (albeit not using the keyword 'typedef' but rather a templated 'using'). At the moment the best way to work around this restriction is by using meta functions – those 'template struct' constructs.

    Though even with template aliases you need meta functions in their declarations if these aliases shall, depending on their template parameters, refer to template classes and ordinary types.

    .filmor schrieb:

    http://www.boost.org/libs/utility/utility.htm#result_of

    That does not apply here because result_of requires a call-expression, using an operator does not necessarily result in a function call.



  • camper schrieb:

    In the upcoming standard, this problem shall have an elegant solution:

    template<typename T> constexpr sqr(T&& x) -> decltype( x * x ) { return x * x; }
    

    for the sake of syntacical correctness:

    template <typename T>
    auto constexpr
    sqrt (T&&x) -> decltype( x*x )
    {
      return x*x;
    }
    


  • SAn schrieb:

    Thanks. How to use __typeof technique?

    depends on how your compiler implements it.
    possibly either this way:

    template <class T>
    typeof (T() + T())
    //or that way (preferably):
    typeof (*static_cast<T*>(0) + *static_cast<T*>(0))
    foo (T const& a, T const& b)
    {
       return a + b;
    }
    


  • Was ist denn jetzt der Unterschied zwischen tr1::result_of und decltype ?

    Fragt
    Werner



  • decltype funktioniert immer, nicht nur mit funktionen.

    int a(1), b(2), c(3);
    decltype (a + b * c) d; //int d;
    decltype (a = 5) e; //int& e; - operator= gibt eine referenz zurück
    

    result_of wird in C++0x übrigens mittels decltype implementiert sein.

    (btw. schreibe ich gerade für das magazin einen artikel über die neuen sprachfeatures von C++0x, also einfach bis nächste woche warten 😉 )



  • Hi SAn,

    SAn schrieb:

    template<class T, class TR> inline TR Sqr(T const &x)
    {
      return x*x;
    }
    

    also does not work 😞 . Compiler error “Cannot deduce template parameter for TR” when trying to call such a function.

    I think the order ist wrong.

    template<class TR, class T> inline TR Sqr(T const &x)
    {
      return x*x;
    }
    
    int main() 
    {
      int i = 3;
      double d = Sqr<double>(i); 
    }
    

    If you want the return value it must be on the first place. Then you must explicit determine the first template and the second parameter is deduced by the compiler.

    BR,
    Markus


Anmelden zum Antworten