Est-il possible d'écrire une version variadique de std::is_convertible? Par exemple, are_convertible<T1, T2, T3, T4> renverrait is_convertible<T1, T3> && is_convertible<T2, T4>. J'y réfléchis depuis quelques heures mais je n'ai rien trouvé de raisonnable.

Pour clarifier, je veux l'utiliser un peu comme ceci:

template <class ...Args1>
struct thing
{
  template <class ...Args2>
  enable_if_t<are_convertible<Args2..., Args1...>::value>
  foo(Args2 &&...args){}
}
8
user697683 5 janv. 2016 à 18:41

3 réponses

Meilleure réponse

Vous n'avez pas besoin de concaténer Args2... et Args1..., et vous ne devriez pas le faire, car faire pour rend impossible de dire où Args2... se termine et Args1... commence. La façon de passer plusieurs arguments variadiques d'une manière qui leur permet d'être extraits individuellement est de les encapsuler dans un autre modèle: étant donné un modèle variadique my_list, vous pouvez structurer votre my_convertible pour être appelé comme

my_convertible<my_list<Args2...>, my_list<Args1...>>

La bibliothèque standard a déjà un modèle variadic qui fonctionne bien ici: tuple. Non seulement cela, mais tuple<Args2...> est convertible en tuple<Args1...> si et seulement si Args2... est convertible en Args1..., vous pouvez donc simplement écrire:

std::is_convertible<std::tuple<Args2...>, std::tuple<Args1...>>

Remarque: dans les commentaires, @ zatm8 signale que cela ne fonctionne pas toujours: std::is_convertible<std::tuple<const char *&&>, std::tuple<std::string &&>>::value est signalé comme false, mais std::is_convertible<const char *&&, std::string &&>::value est signalé comme true.

Je crois que c'est un bug, qu'ils devraient tous les deux être signalés comme true. Le problème est reproductible sur http://gcc.godbolt.org/ avec clang 3.9.1. Il n'est pas reproductible avec gcc 6.3, et il n'est pas non plus reproductible avec clang 3.9.1 lors de l'utilisation de -stdlib=libc++. Il semble que libstdc ++ utilise une fonctionnalité de langage que clang ne gère pas correctement, et le réduire à un court exemple qui ne repose pas sur les en-têtes de bibliothèque standard donne:

struct S {
  S(const char *) { }
};
int main() {
  const char *s = "";
  static_cast<S &&>(s);
}

Ceci est accepté par gcc, mais rejeté par clang. Il a été signalé en 2014 sous le nom de https://llvm.org/bugs/show_bug.cgi ? id = 19917.

Il semble que cela ait été corrigé à la fin de 2016, mais le correctif ne l'a pas encore fait dans une version publiée: http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161031/175955.html

Si cela vous affecte, vous voudrez peut-être éviter std::tuple et utiliser la réponse de @ Yakk à la place.

12
26 janv. 2017 à 21:45

Oui.

Tout d'abord, comment le faire. Alors, pourquoi vous ne devriez pas le faire.

Comment faire:

Ecrire regroup, qui prend une liste de kN éléments et la regroupe en N groupes de k, entrelacés. Les groupes peuvent être template<class...>struct types{};.

Ensuite, écrivez apply, qui prend un template<class...>class Z et un class... de groupes (aka types<...>), et applique Z au contenu de chacun des bundles, renvoyant un {{ X4}} du résultat.

Puis repliez le contenu de types<...> en utilisant template<class A, class B> struct and_types:std::integral_constant<bool, A{}&&B{}>{};.

Je trouverais cela pratiquement inutile, donc je ne vais pas le mettre en œuvre. Cela devrait être facile avec une bibliothèque de métaprogrammation décente, la plupart des opérations ci-dessus sont standard.


Pourquoi tu ne devrais pas

Mais vraiment, compte tenu de votre exemple, faites simplement ceci:

template<class...Ts>
struct and_types:std::true_type{};
template<class T0, class...Ts>
struct and_types<T0,Ts...>:std::integral_constant<bool, T0{} && and_types<Ts...>{}>{};

Alors:

std::enable_if_t<and_types<std::is_convertible<Args2, Args1>...>{}>

Fait le travail. Tout le brassage n'est que du bruit.

Avec le support de fold ... de C ++ 1z, nous nous débarrassons également de and_types et utilisons simplement && et ....

7
Community 20 juin 2020 à 09:12

Vous pouvez utiliser std :: conjuction pour plier tous les types de résultats en un seul:

template <class... Args>
struct is_convertible_multi {
   constexpr static bool value = false;
};

template <class... Args1, class... Args2>
struct is_convertible_multi<::std::tuple<Args1...>, ::std::tuple<Args2...>> {
   constexpr static bool value =  ::std::conjunction_v<::std::is_convertible<Args1,Args2>...>;
};

Et n'oubliez pas de vérifier si

sizeof... (Args1) == sizeof... (Args2)

Utilisation de if constexpr ou enable_if

0
zatm8 26 janv. 2017 à 10:26