#include <iostream>

struct bar;					// forward declaration

class foo {					// implicitly private
	friend struct ::bar;	// but bar is a friend

	template<typename T = int>
	static void static_function_template() {}

	template<typename T = int>
	void function_template() {}

	template<typename T = int>
	class class_template {
		static void static_function() {}
		void function() {}
		static T static_value;
		T value;
	};

};

struct bar {				// implicitly public

	// some alias templates referring to class foo:
	template<typename T> using type01 = decltype(foo::static_function_template<T>());
	template<typename T> using type02 = decltype(foo::static_function_template<>());
	template<typename T> using type03 = decltype(foo::static_function_template<T>);
	
	template<typename T> using type04 = decltype(foo().function_template<T>());
	template<typename T> using type05 = decltype(foo().function_template<>());

	template<typename T> using type06 = decltype(foo::class_template<T>::static_function());
	template<typename T> using type07 = decltype(foo::class_template<>::static_function());
	template<typename T> using type08 = decltype(foo::class_template<T>::static_function);

	template<typename T> using type09 = decltype(foo::class_template<T>().function());
	template<typename T> using type10 = decltype(foo::class_template<>().function());

	// check (has direct access to bar)
	template<template<typename> class TT, typename T, typename = TT<T>>
	static void check(int) { std::cout << "> can be instantiated       "; }
	template<template<typename> class TT, typename T>
	static void check(...) { std::cout << "> can NOT be instantiated   "; }
};

// check (has no direct access to bar)
template<template<typename> class TT, typename T, typename = TT<T>>
void check(int) { std::cout << "> can be instantiated       "; }
template<template<typename> class TT, typename T>
void check(...) { std::cout << "> can NOT be instantiated   "; }



int main() {
#ifdef __clang__
	std::cout << "-- compiled with clang --" << std::endl;
#endif
#ifdef _MSC_VER
	std::cout << "-- compiled with MSVC --" << std::endl;
#endif
#ifdef __GNUC__
	std::cout << "-- compiled with GCC --" << std::endl;
#endif
	std::cout << std::endl;
	
	std::cout << "         inside of bar:              outside of bar:             " << std::endl;
	std::cout << "type01   "; bar::check<bar::type01, int>(0); check<bar::type01, int>(0); std::cout << std::endl;
	std::cout << "type02   "; bar::check<bar::type02, int>(0); check<bar::type02, int>(0); std::cout << std::endl;
	std::cout << "type03   "; bar::check<bar::type03, int>(0); check<bar::type03, int>(0); std::cout << std::endl;
	std::cout << "type04   "; bar::check<bar::type04, int>(0); check<bar::type04, int>(0); std::cout << std::endl;
	std::cout << "type05   "; bar::check<bar::type05, int>(0); check<bar::type05, int>(0); std::cout << std::endl;
	std::cout << "type06   "; bar::check<bar::type06, int>(0); check<bar::type06, int>(0); std::cout << std::endl;
	std::cout << "type07   "; bar::check<bar::type07, int>(0); check<bar::type07, int>(0); std::cout << std::endl;
	std::cout << "type08   "; bar::check<bar::type08, int>(0); check<bar::type08, int>(0); std::cout << std::endl;
	std::cout << "type09   "; bar::check<bar::type09, int>(0); check<bar::type09, int>(0); std::cout << std::endl;
	std::cout << "type10   "; bar::check<bar::type10, int>(0); check<bar::type10, int>(0); std::cout << std::endl;
	std::cout << std::endl;

	return 0;
}