Je n'arrive pas à comprendre comment implémenter une fonction avec un nombre variable d'arguments du même type.
J'écris pour un microcontrôleur avec peu de pile et de mémoire, donc je ne peux pas utiliser la récursivité ou le STL (les parties avec des exceptions).
Est-il possible de créer une telle fonction?
S s; const int mode=3, speed=1; fun<&s,1,2,7,4>(mode,speed);
qui se développe en quelque chose comme ceci:
for(int arg:args){ s->r1+=7*arg; }
exemple d'appel:
struct S{ int r1; int r2; }; template<S* s, int... args> fun(int arg1, int arg2);
3 Réponses :
Malheureusement, il n'existe actuellement aucun moyen de spécifier un pack de paramètres de fonction où chaque paramètre est du même type. La prochaine meilleure chose que vous puissiez obtenir (pour autant que je sache) serait une fonction qui prend un nombre variable d'arguments de n'importe quel type tant que le type de tous est int
: XXX
L'astuce ici est de s'appuyer sur SFINAE pour supprimer efficacement toutes les versions de la fonction où tous les arguments
ne finissent pas par être de type int
…
Pour votre exemple concret, vous pourriez faire, par exemple:
#include <type_traits> struct S { int r1; int r2; }; template <S& s, typename... Args> auto f(Args... args) -> std::enable_if_t<(std::is_same_v<Args, int> && ...)> { ((s.r1 += 7 * args), ...); } S s; const int mode=3, speed=1; void test() { f<s>(mode, speed); }
Je n'arrive pas à comprendre comment implémenter une fonction avec un nombre variable d'arguments du même type.
Argument de modèle du même type ou arguments de fonction ordinaires du même type?
Le premier cas est simple (si le type est un type admis pour les types de valeur de modèle), exactement comme vous l'avez écrit p>
#include <utility> #include <type_traits> struct S { int r1; int r2; }; S s; const int mode=3, speed=1; template <typename T, std::size_t> using getType = T; template <std::size_t N, typename = std::make_index_sequence<N>> struct bar; template <std::size_t N, std::size_t ... Is> struct bar<N, std::index_sequence<Is...>> { static constexpr auto f (getType<int, Is> ... args) { ((s.r1 += 7 * args), ...); } }; template <S &, std::size_t N = 64u, typename = std::make_index_sequence<N>> struct foo; template <S & s, std::size_t N, std::size_t ... Is> struct foo<s, N, std::index_sequence<Is...>> : public bar<Is>... { using bar<Is>::f...; }; int main () { foo<s>::f(mode, speed); }et vous pouvez les utiliser en utilisant le pliage de modèle, si vous pouvez utiliser C ++ 17,
fun(1, 2, 3l); // compilation error (3l is a long int, not an int)ou dans un peu plus compliqué avant (C ++ 11 / C ++ 14)
template <typename ... Args> auto fun (Args ... args)Malheureusement, vous pouvez appeler ce type de fonction avec des entiers connus au moment de la compilation donc, par exemple, pas avec variables
int a = 7; fun<&s,1,2,a,4>(mode,speed); // compilation errorDans ce cas, vous avez besoin d'une liste variadique d'arguments de fonction ordinaires du même type; malheureusement c'est un peu plus compliqué.
Vous pouvez créer une liste variadique typique de paramètres de modèle
template <S* s, int... args> auto fun (int arg1, int arg2) { using unused = int[]; (void)unused { 0, s->r1 += 7 * args ... }; }en imposant, via SFINAE, que tout
Les arguments ...
sont déduits ou expliqués commeint
(voir la réponse de Michael Kenzel).Malheureusement, cela nécessite que chaque argument soit exactement si le type
int donc appeler func avec (par exemple) un
long int
donne une erreur de compilationtemplate <S* s, int... args> auto fun (int arg1, int arg2) { ((s->r1 += 7 * args), ...); }Vous pouvez évidemment relâcher la condition SFINAE imposant (en exemple) que tous les types
Args ...
sont convertibles (std :: is_convertible
) enint
mais n'a pas exactement le développement d'une fonction recevant un nombre variadique d'arguments du même type.Si vous pouvez accepter une limite supérieure au nombre d'arguments (
64
, dans l'exemple suivant) et que la fonction est méthode (peut-être statique) d'une classe, vous pouvez créer une classefoo
contenant une méthodef ()
qui reçoit zéroint
, unf ()
qui reçoit unint
, unf ()
qui reçoit deuxint
s, etc., jusqu'à ce qu'unf ()
qui reçoive 63int
s.Ce qui suit est une compilation complète de C ++ 17 exemple
template<S* s, int... args> fun (int arg1, int arg2);En C ++ 14, c'est un peu plus compliqué car il n'y a pas de variadique
utilisant
donc vous devez écrire lefoo
class de manière récursive.En C ++ 11, vous devez également développer un substitut pour
std :: make_index_sequence
/std :: index_sequence
.
Faire en sorte que tous les paramètres d'un pack variadique soient du même type peut être requis avec les concepts C ++ 20.
Malheureusement, à partir de C ++ 20, la bibliothèque standard n'a pas de concept pour all_same (il n'y a que std :: same_as
pour deux types), mais il peut être facilement défini:
template <typename AlwaysVoid, typename... Ts> struct has_common_type_impl : std::false_type {}; template <typename... Ts> struct has_common_type_impl<std::void_t<std::common_type_t<Ts...>>, Ts...> : std::true_type {}; template <typename... Ts> concept has_common_type = sizeof...(Ts) < 2 || has_common_type_impl<void, Ts...>::value; template<typename... Ts> requires has_common_type<Ts...> void foo(Ts&&... ts) {}
Code: https://godbolt.org/z/dH9t-N
Notez que dans de nombreux cas, il n'est pas nécessaire d'exiger le même type, vous pouvez à la place exiger que tous les arguments aient un type commun . Pour exiger un type commun, basé sur tester si un type commun existe em > vous pouvez avoir le concept suivant:
template<class... Ts> concept all_same = sizeof...(Ts) < 2 || std::conjunction_v< std::is_same<std::tuple_element_t<0, std::tuple<Ts...>>, Ts>... >; template<typename... Ts> requires all_same<Ts...> void foo(Ts&&... ts) {}
le truc du tuple est simplement: template
une version _t de head: template
donnant: template
template
@QuentinUK est soit same_as
et is_same
meilleur que l'autre et comment?
int ... args
est correct. Avez-vous un code réel qui l'utilise qui ne fonctionne pas?@logicstuff Cela existe depuis C ++ 11. Vous pouvez avoir un pack de types ou de valeurs.