Variadic template that determines the best conversion

How can we implement a variational pattern that defines the type T and the list of types E 1 , E 2 , ... E N , determines the type of list for which the conversion from T to this type, in accordance with the resolution of the overload, is the best?

void should be the result if the best conversion does not exist - in other words, when either there is ambiguity, or T cannot be converted to any type in the list.
Please note that this means that our template must be SFINAE-friendly, i.e. It is not a blunder when the best conversion does not exist.

The following static_assert should succeed:

 static_assert( std::is_same< best<int, long, short>, void >{}, "" ); static_assert( std::is_same< best<int, long, std::string>, long >{}, "" ); static_assert( std::is_same< best<int>, void >{}, "" ); 

(Assuming for simplicity that best is an alias pattern related to the actual pattern)

This case is not defined:

 static_assert( std::is_same< best<int, int, int>, ???>{}, "" ); 

Either void or int should be acceptable here. (If the latter is selected, we can still check the shell template to see if the result type will be contained twice in the list, and if it is, print void ).

+5
source share
1 answer

My best approach currently:

 #include <type_traits> template <class T> using eval = typename T::type; template <class T> struct identity {using type = T;}; template <typename T, typename... E> class best_conversion { template <typename...> struct overloads {}; template <typename U, typename... Rest> struct overloads<U, Rest...> : overloads<Rest...> { using overloads<Rest...>::call; static identity<U> call(U); }; template <typename U> struct overloads<U> { static identity<U> call(U); }; template <typename... E_> static identity<eval<decltype(overloads<E_...>::call(std::declval<T>()))>> best_conv(int); template <typename...> static identity<void> best_conv(...); public: using type = eval<decltype(best_conv<E...>(0))>; }; template <typename... T> using best_conversion_t = eval<best_conversion<T...>>; 

Demo For the “unspecified” case above, this pattern will give you an int .

The main idea is to put a bunch of overloaded functions with one parameter in different areas where the name search will look for, with the parameter and the return type of each overload corresponding to one of the types in our list.
overloads accomplishes this task by recursively introducing one call declaration at a time and adapting all previously entered call from the base specializations through using declarations. Thus, all call are in different areas, but are considered the same when it comes to resolving congestion.

Then apply SFINAE in the best_conv function template to check if the call call correctly formed (inside overloads ): if so, take the return type (which, by definition, is the parameter type) from the selected declaration and use it as our result - it will be that the type we are looking for.
Also provide a second best_conv overload, which returns void and can be selected by default (when SFINAE is applied to the first overload and pushes it out of the candidate set).

Inverse types use identity<> to avoid type decomposition when working with, for example. array or types of function pointers.

+6
source

Source: https://habr.com/ru/post/1208515/


All Articles