15

I am reading this question on isocpp FAQ here, this question is explaining how to write the return type for ???

template<class T, class U>
??? mul(T x, U y)
{
return x*y;
}

I understand the easy way is to write auto mul(T x, U y) -> decltype(x*y), however the question also gives another way, which is to replace ??? by decltype(*(T*)(0)**(U*)(0)). But I don't fully understand what this decltype(*(T*)(0)**(U*)(0)) is really doing, it seems that it is declaring a temporary pointer T* and initialize it to zero and then dereference the pointer, then multiplied by the same counterpart for type U, is my understanding right?

But why using pointers? I think decltype(T(0)*U(0)) or decltype(T{0}*U{0}) should also work.

Allanqunzi
  • 3,230
  • 1
  • 26
  • 58
  • 2
    That assumes that `T` and `U` have a constructor that takes a single integer parameter. The pointer way makes no assumptions about the constructors of `T` and `U`. – Colonel Thirty Two Sep 08 '15 at 15:56
  • 4
    Note, it is not undefined behavior since it is [in an unevaluated context](http://stackoverflow.com/a/28723577/1708801) – Shafik Yaghmour Sep 08 '15 at 15:58

2 Answers2

21
decltype(*(T*)(0)**(U*)(0))

Let's split it up:

(T*)(0) //cast a null pointer to a T*
*(T*)(0) //dereference, giving a T

(U*)(0) //cast a null pointer to a U*
*(U*)(0) //dereference, giving a U

(*(T*)(0)) * (*(U*)(0)) //the result of multiplying a U and a T

decltype(T(0)*U(0)) is equivalent only if T and U have constructors taking a single int (or something which can be implicitly converted to from an integer literal).

The standard library already has a std::declval to do this in a cleaner fashion:

decltype(std::declval<T>() * std::declval<U>())
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
0

this indeed error prone.

in your solution , you basically assume the construcor can get 0 as argument, and that may not be the case.

the snippet basically cast NULL to some T object pointer, then pull out T with the * operator, now we left with basically decltype({T object}*{U object}) it assumes that in compile time, the decltype will be replaced by the real type and in run time there will be no pointers. but this code is very ugly and un-maintainable.

a better solution is to use C++11 std::result_of :
typename std::result_of<declval<T>()*declval<U>()>::type

declval does something similar to *(T*)(0) only it returns r-value reference (meaning T&&)

David Haim
  • 25,446
  • 3
  • 44
  • 78