Updates to Expression SFINAE in VS 2017 RC

Throughout the VS 2015 cycle we’ve been focusing on the quality of our expression SFINAE implementation. Because expression SFINAE issues can be subtle and complex we’ve been using popular libraries such as Boost and Microsoft’s fork of Range-v3 to validate our implementation and find remaining bugs. As we shift the compiler team’s focus to Visual Studio 2017 release we’re excited to tell you about the improvements we’ve made in correctly parsing expression SFINAE.

We’ve been tracking the changes and improvements to our parsing of expression SFINAE throughout the Visual Studio 2015 and 2017 cycle. The improvements added to VS 2017 RC (since VS 2015 Update 3) are listed below. We’ve also updated the original blog post with our recent improvements so you can track all of our progress in one place.

Improvements since Visual Studio 2015 Update 3

We now correctly compile code that constructs temporary objects as Range-v3 does extensively:

		#include <type_traits>
		
		template<typename T, std::enable_if_t<std::is_integral<T>{}> * = nullptr>
		char f(T *);
		
		template<typename T>
		short f(...);
		
		int main()
		{
			static_assert(sizeof(f<int>(nullptr)) == sizeof(char), "fail");
			static_assert(sizeof(f<int *>(nullptr)) == sizeof(short), "fail");
		}

We’ve also improved access checks for SFINAE which are illustrated in this code sample:

		template <typename T> class S {
		private:
			typedef T type;
		};
		
		template <typename T> class S<T *> {
		public:
			typedef T type;
		};
		
		template <typename T, typename S<T>::type * = nullptr>
		char f(T);
		
		template<typename T>
		short f(...);
		
		int main()
		{
			static_assert(sizeof(f<int>(0)) == 2, "fail"); // fails in VS2015
			static_assert(sizeof(f<int *>(nullptr)) == 1, "fail");
		}

Lastly, we’ve improved support for void_t when used inside of a typename as found in Boost Hana:

		template<typename T, typename U>
		struct std_common_type {};
		
		template<typename T>
		struct std_common_type<T, T> { using type = T; };
		
		template<typename T, typename U>
		struct is_same { static const bool value = false; };
		
		template<typename T>
		struct is_same<T, T> { static const bool value = true; };
		
		template<bool, typename T>
		struct enable_if {};
		
		template<typename T>
		struct enable_if<true, T> { using type = T; };
		
		template<typename...> using void_t = void;
		
		template <typename T, typename U = T, typename = void>
		struct EqualityComparable1 { static const bool value = false; };
		
		template <typename T, typename U>
		struct EqualityComparable1<T, U, typename enable_if<!is_same<T, U>::value, void_t<typename std_common_type<T, U>::type>>::type>
		{
			static const bool value = true;
		};
		
		template <typename T, typename U = T, typename = void>
		struct EqualityComparable2 { static const bool value = false; };
		
		template <typename T, typename U>
		struct EqualityComparable2<T, U, void_t<typename std_common_type<T, U>::type>>
		{
			static const bool value = true;
		};
		
		void f()
		{
			struct S1 {};
			struct S2 {};
			static_assert(!EqualityComparable1<S1, S2>::value, "fail"); // fails in VS2015
			static_assert(!EqualityComparable2<S1, S2>::value, "fail");
		}

In closing

As always, we welcome your feedback. Please give us feedback about expression SFINAE in the comments below or through e-mail at visualcpp@microsoft.com.

If you encounter other problems with Visual C++ in VS 2017 RC please let us know via the Report a Problem option, either from the installer or the Visual Studio IDE itself. For suggestions, let us know through UserVoice. Thank you!