
Rambling a little bit today... I set up a little test: template <class T> struct A { A(const T&) {} }; template <class T> inline A<T> make_A(T t) { return A<T>(t); } void f() {} int main() { std::cout << typeid(make_A(0)).name() << '\n'; const volatile int i = 0; std::cout << typeid(make_A(i)).name() << '\n'; const int a3[3] = {}; std::cout << typeid(make_A(a3)).name() << '\n'; int a4[4] = {}; std::cout << typeid(make_A(a4)).name() << '\n'; std::cout << typeid(make_A(f)).name() << '\n'; std::cout << typeid(make_A("narrow")).name() << '\n'; std::cout << typeid(make_A(L"wide")).name() << '\n'; } This prints out (on my system): A<int> A<int> A<const int *> A<int *> A<void (*)()> A<const char *> A<const wchar_t *> I believe this output, using pass-by-value for make_A, can be considered "the gold standard" for decay<T>. And here's what you get if make_A simply uses pass-by-const-ref: template <class T> inline A<T> make_A(const T& t) { return A<T>(t); } A<int> A<volatile int> A<int[3]> A<int[4]> A<void ()> A<char[7]> A<wchar_t[5]> Using a decay that supports function->function pointer, and one that strips top level cv-qualifiers off of non-arrays, and non-functions, and using John's overload strategy: template <class T> inline A<typename decay<T>::type> make_A(T& t) { return A<typename decay<T>::type>(t); } template <class T> inline A<typename decay<const T>::type> make_A(const T& t) { return A<typename decay<const T>::type>(t); } I can get the original behavior: A<int> A<int> A<const int *> A<int *> A<void (*)()> A<const char *> A<const wchar_t *> As Thorsten points out, remove_reference has no bearing as T can not be deduced as a reference type in C++03. But fwiw, I can achieve this output with only one make_A overload using the rvalue reference (if now decay has remove_reference). template <class T> inline A<typename decay<T>::type> make_A(T&& t) { return A<typename decay<T>::type>(std::forward<T>(t)); } The single overload: template <class T> inline A<typename decay<T>::type> make_A(T& t) { return A<typename decay<T>::type>(t); } gets nearly a perfect score, handling const qualified objects correctly. Its only failing is that it won't bind to rvalues. The const T& overload by itself doesn't work either because it gets arrays wrong (turns them into const T*). This exposes a weakness in the overload strategy: It doesn't scale well. Consider John's suggested make_pair: make_pair( const F& f, const S& s ); make_pair( F& f, S& s ); This fails with: int a4[4] = {}; std::cout << typeid(std::make_pair(0, a4)).name() << '\n'; std::pair<int, const int *> intead of: std::pair<int, int *> You really need four make_pair overloads to cover everything: make_pair( const F& f, const S& s ); make_pair( const F& f, S& s ); make_pair( F& f, const S& s ); make_pair( F& f, S& s ); Now you should get the correct: std::pair<int, int *> In contrast, only one make_pair is required when using rvalue_references: make_pair( F&& f, S&& s ); -Howard