1
votes

Conflit de surcharge C ++ sur l'argument de modèle exact

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)


0 commentaires

3 Réponses :


1
votes

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.


0 commentaires

1
votes

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...);}


3 commentaires

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 ) || ...), bool> = true 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é.



0
votes

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...} {}
};

0 commentaires