bar
défini ci-dessous permet deux types d'initalisations (le modèle de bar
sera toujours plusieurs int
en cours d'utilisation)
bar<int> s(3);
Ça a l'air bien; cependant si le modèle est exactement un int
, ce code est ambigu pour le compilateur (bien que les deux compréhensions aient la même signification):
template<class C> inline void store(C dst) {} template<class T, class C, class... E> inline void store(C dst, T val, E... etc) { *dst = val; store(++dst, etc...); } template<class... T> class bar { static const int n = sizeof...(T); int data[n]; public: bar(int x) {for(int i=0; i<n; i++) data[i]=x;} bar(T... x) {store(data,x...);} };
Le seul moyen d'éviter cela à la spécialisation est-il le seul cas int
? (Cela rend de toute façon le code plus complexe)
3 Réponses :
Que diriez-vous de transformer le deuxième constructeur en un modèle et que SFINAE ne l'active que lorsque T ...
n'est pas int
?
Je veux dire quelque chose comme
template <std::size_t N = sizeof...(T), typename = std::enable_if_t< (N != 1u) || (false == std::is_same<std::tuple<int, T...>, std::tuple<T..., int>>{})>> bar(T... x) {store(data,x...);}
Évidemment, si vous ne pouvez utiliser que C ++ 11, vous devez utiliser typename std :: enable_if :: type
au lieu de std :: enable_if_t .
Si vous pouvez utiliser C ++ 17, vous pouvez utiliser le pliage de modèle, pour vérifier que T ...
n'est pas 't int
, comme suggéré par Jans.
Vous pouvez désactiver le constructeur variadique lorsqu'il n'y a qu'un seul argument et qu'il s'agit d'un int
.
Si vous avez c ++ 17, vous pouvez le faire comme
template <bool... Pred> struct all_dummy; template <bool... Preds> using all = std::is_same<all_dummy<Preds...>, all_dummy<((void)Preds, true)...>>; template < std::size_t N = sizeof...(T), std::enable_if_t<(N != 1 || !all<std::is_same<T, int>::value...>::value), bool> = true > bar(T... x) {store(data,x...);}
Désolé mais ... ne devrait pas être désactivé lorsque T
est int
? Je veux dire ... ne devrait pas être std :: enable_if_t <(N! = 1) || ((false == std :: is_same_v
ou quelque chose de similaire?
Maintenant je suis d'accord. Mais (1) N == 1
me semble superflu (si N! = 1
la condition est vraie grâce au N! = 1
précédent ) et (2) il n'y a pas de réelle différence (car sizeof ... (T) == 1
) mais il me semble que cela devrait être ((! std :: is_same_v
au lieu de ((! Std :: is_same_v
; Je veux dire: la condition doit être vraie aussi quand seul un type est différent de int
, pas seulement quand tous les types sont différents de int
(mais, je le répète: avec N == 1
il ne devrait pas y avoir de réelle différence)
@ max66 - Oui, je m'en suis rendu compte et je l'ai supprimé.
Le seul moyen d'éviter cela à la spécialisation est-il le seul cas int?
Non comme indiqué avec une autre réponse avec SFINAE.
C ++ 20 permettrait même une meilleure syntaxe avec
requires
:template <class... Ts> class bar_impl { protected: static const int n = sizeof...(Ts); int data[n]; public: bar(Ts... xs) : data{xs...} {} }; template <> class bar_impl<int> { static const int n = 1; int data[n]; }; template <class... Ts> class bar : bar_impl<Ts...> { public: using bar_impl<Ts...>::bar_impl; bar(int x) { std::fill(std::begin(data), std::end(data), x);} // ... };(Cela rend de toute façon le code plus complexe)
Pas vraiment d'accord par rapport à SFINAE, avec spécialisation, ça pourrait être:
template <class... Ts> class bar { static const int n = sizeof...(Ts); int data[n]; public: bar(int x) { std::fill(std::begin(data), std::end(data), x);} bar(Ts... xs) requires (n != 1) : data{xs...} {} };