2017-07-31 1 views
6

J'ai typename T1 et j'ai un pack de paramètres typename... Variadic.Struct qui contient un alias au premier type convertible d'un pack variadique

Je souhaite créer une structure qui contient un alias d'utilisation using Type = ... au premier type du jeu de paramètres dans lequel T1 peut être converti. Jusqu'à présent, je l'ai essayé ce qui suit:

template<typename T1, typename T2, typename... Variadic> 
struct VariadicConvertibleType 
{ 
    using Type = std::enable_if<std::is_convertible<T1, T2>::value, T2>::type; 
}; 

Cela pourrait être une solution potentielle à l'aide SFINAE pour les deux premiers types, mais je dois développer ce à tous les types dans le paquet à l'aide de la récursivité. Toutes mes tentatives ont échoué jusqu'ici, parce que vous ne pouvez pas mettre conditionals en utilisant des déclarations d'alias. Sinon, quelque chose de similaire à ce qui pourrait être utilisé:

template<typename T1, typename T2, typename... Variadic> 
struct VariadicConvertibleType 
{ 
    using Type = std::is_convertible<T1, T2>::value ? T2 : VariadicConvertibleType<T1, Variadic...>::Type; 
}; 

Je peux utiliser tout jusqu'à (et y compris) 14 C++ pour mettre en œuvre la solution. Je ne peux pas utiliser autre chose que la bibliothèque standard.

+2

http://fr.cppreference.com/w/cpp/types/conditionnel peut-être? Et quel devrait être le cas de base pour les types compatibles? – LogicStuff

Répondre

4

Vous pouvez utiliser std::conditional, comme ceci:

template<typename T1, typename T2, typename... Variadic> 
struct VariadicConvertibleType 
{ 
    using type = std::conditional_t<std::is_convertible<T1, T2>::value, T2, typename VariadicConvertibleType<T1, Variadic...>::type>; 
}; 

template<typename T1, typename T2> 
struct VariadicConvertibleType<T1, T2> 
{ 
    static_assert(std::is_convertible<T1, T2>::value); 
    using type = T2; 


    // Alternative base-case 
    // using type = std::conditional_t<std::is_convertible<T1, T2>::value, T2, T1>; 
}; 

J'ai fourni deux bases-cas (remplacement d'un commentaire dans). Le primaire (que je chose est ce que vous voulez) utilise un (C++ 14) static_assert si T1 n'est convertible en aucun des types en Variadic. Le cas de base alternatif définit type à T1 dans ce cas.

test

#include <iostream> 
#include <type_traits> 
#include <typeinfo> 

int main() 
{ 
    using my_type_1 = typename VariadicConvertibleType<int, double, float>::type; 
    std::cout << typeid(my_type_1).name() << '\n'; // double 

    using my_type_2 = typename VariadicConvertibleType<int, int*, float>::type; 
    std::cout << typeid(my_type_2).name() << '\n'; // float 

    using my_type_3 = typename VariadicConvertibleType<int, int*, float*>::type; 
    std::cout << typeid(my_type_3).name() << '\n'; // Complile error with the primary base-case, and int with the alternative base-case. 
} 
+1

La solution 'static_assert' ne semble pas fonctionner pour moi, mais plutôt l'alternative de base. Gloire. – PandarinDev

0

Le problème avec std::conditional est qu'il requiert à la fois les types que vous nourrissez à être bien définis. Pour contourner cela, vous devez introduire une sorte d'indirection dans le processus. Pour ce faire, nous aurons un template d'aide qui, donné un booléen, définira le type ou se rabattra sur l'élément suivant.

namespace details { 

template <bool B, typename T, typename... Args> 
struct FirstConvertibleImpl; 

template <typename T, typename U, typename... Args> 
struct FirstConvertibleImpl<true, T, U, Args...> { 
    using type = U; 
}; 

template <typename T, typename U, typename V, typename... Args> 
struct FirstConvertibleImpl<false, T, U, V, Args...> { 
    using type = typename FirstConvertibleImpl<std::is_convertible<T, V>::value, T, V, Args...>::type; 
}; 

} 

Si les types donnés ne conduisent pas à une conversion valide, il émettra évidemment une erreur que nous ne tenions pas à définir les cas « invalide » (FirstConvertibleImpl<false, T1, T2> n'a pas mise en œuvre pour tous les types T1, T2)

maintenant, nous faisons une interface de niveau supérieur pour une utilisation plus claire:

template <typename T, typename... Args> 
struct FirstConvertibleInPack; 

template <typename T, typename U, typename... Args> 
struct FirstConvertibleInPack<T, U, Args...> { 
    using type = typename details::FirstConvertibleImpl<std::is_convertible<T, U>::value, T, U, Args...>::type; 
}; 

Encore une fois, nous ne se soucient pas de définir les cas où Args... = [] parce que ces cas sont mal formés de toute façon. Un Live Demo peut être trouvé sur Coliru. Bien sûr, vous pouvez encore plus abstrait et rendre cet exemple plus générique en permettant d'appliquer tous les traits binaires qui vous plaisent.