Search code examples
c++templatesc++11function-templatesdefault-arguments

Template parameter default to a later one


This link doesn't answer my question so I'll ask it here:

Basically I want to write a template function

template <typename Out, typename In>
Out f(In x);

Here I always need to specify Out when calling f. I don't want to do it every time, so I basically want

template <typename Out = In, typename In>
Out f(In x);

Which means if I don't specify Out, it will default to In. However, this is not possible in C++11.

So my question is, is there any way to achieve the effect:

  1. calling f(t) will instantiate f<T,T>(t) or more generally f<typename SomeThing<T>::type, T>
  2. calling f<U>(t) will instantiate f<U, T>(t)

Solution

  • I have a PERFECT solution here! f<const int&> won't work because a function can't return a reference to a temporary, not related to the techniques used here.

    [hidden]$ cat a.cpp
    #include <iostream>
    #include <type_traits>
    #include <typeinfo>
    using namespace std;
    
    template <typename Out, typename In>
    Out f_impl(In x) {
      cout << "Out=" << typeid(Out).name() << " " << "In=" << typeid(In).name() << endl;
      return Out();
    }
    
    template <typename T, typename... Args>
    struct FirstOf {
      typedef T type;
    };
    
    template <typename T, typename U>
    struct SecondOf {
      typedef U type;
    };
    
    template <typename... Args, typename In>
    typename enable_if<sizeof...(Args) <= 1, typename FirstOf<Args..., In>::type>::type f(In x) {
      typedef typename FirstOf<Args..., In>::type Out;
      return f_impl<Out, In>(x);
    }
    
    template <typename... Args, typename In>
    typename enable_if<sizeof...(Args) == 2, typename FirstOf<Args...>::type>::type f(In x) {
      typedef typename FirstOf<Args...>::type Out;
      typedef typename SecondOf<Args...>::type RealIn;
      return f_impl<Out, RealIn>(x);
    }
    
    int main() {
      f(1);
      f(1.0);
      f<double>(1);
      f<int>(1.0);
      f<int>(1);
      f<const int>(1);
      f<int, double>(1);
      f<int, int>(1);
      f<double, double>(1);
    }
    [hidden]$ g++ -std=c++11 a.cpp
    [hidden]$ ./a.out
    Out=i In=i
    Out=d In=d
    Out=d In=i
    Out=i In=d
    Out=i In=i
    Out=i In=i
    Out=i In=d
    Out=i In=i
    Out=d In=d