#include <iostream>

    // test stubs:
    template<typename... Ts>
    using UPtr = int;
    template<typename... Ts>
    using SPtr = double;
    
    template<typename T, typename...TA>
    UPtr<T,TA...> makeUniquePtr() {
    	std::cout << "makeUniquePtr\n";
    	return {};
    }
    template<typename T, typename...TA>
    SPtr<T,TA...> makeSharedPtr() {
    	std::cout << "makeSharedPtr\n";
    	return {};
    }
    
    struct unique_ptr_maker {
      template<typename T, typename...TA, typename...Args>
      static UPtr<T, TA...> make(Args&&...args) {
        return makeUniquePtr<T, TA...>( std::forward<Args>(args)... );
      }
    };
    struct shared_ptr_maker {
      template<typename T, typename...TA, typename...Args>
      static SPtr<T, TA...> make(Args&&...args) {
        return makeSharedPtr<T, TA...>( std::forward<Args>(args)... );
      }
    };
    
template<typename maker, class T, class... TA> void func() {
	std::cout << "func\n";
	maker::template make<T, TA...>();
}

template<class T, class... TA> void implementation1()
{
	func<unique_ptr_maker, T, TA...>();
}
template<class T, class... TA> void implementation2()
{
	func<shared_ptr_maker, T, TA...>();
}
int main() {
	implementation1<int, double>();
	implementation2<int, char>();
	return 0;
}