Comment puis-je empêcher C ++ de deviner un deuxième argument de modèle?

26

J'utilise une bibliothèque C ++ ( strf ) qui, quelque part en elle, a le code suivant:

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

Maintenant, je veux utiliser strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)dans mon code. Mais si je le fais, j'obtiens l'erreur suivante (avec le NVCC de CUDA 10.1):

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

Le code de la bibliothèque peut probablement être modifié pour éviter cela (par exemple en utilisant:

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

s'assurer Rangen'est pas un pointeur); mais je ne peux pas faire ce changement pour le moment. Au lieu de cela, je veux en quelque sorte indiquer au compilateur que je veux vraiment vraiment n'avoir qu'un seul argument de modèle, pas un spécifié et un autre déduit.

Puis-je faire cela?

Souhaiterait des réponses pour C ++ 11 et C ++ 14; Les réponses C ++ 17 impliquant des guides de déduction sont moins pertinentes mais si vous en avez un, veuillez le poster (pour les futures versions de NVCC ...)


Mise à jour: la bibliothèque strf elle-même a été mise à jour pour contourner cette situation, mais la question demeure telle qu'elle a été posée.

einpoklum
la source
1
Je suppose que passer un itérateur personnalisé qui enveloppe finement char*mais n'en est-il pas un n'est pas une solution?
Konrad Rudolph
1
@KonradRudolph: C'est une solution de contournement, mais ne répond pas à ma question. En fait, j'ai déjà une autre solution de contournement (spécifique à ce qui est dans le /*...*/), mais j'aimerais prendre la route ici.
einpoklum
1
Dans ce cas, ma réponse (supposée) est «impossible», malheureusement. Pour être juste, je ne suis pas sûr d'accepter ma solution de contournement suggérée dans mon propre code.
Konrad Rudolph
Juste pour clarifier: voulez-vous une solution générale, qui fonctionnerait toujours pour différencier un appel entre des surcharges de modèle avec un vs deux paramètres ou voulez-vous seulement une solution spécifique à ce cas?
noyer
@walnut: Une solution générale serait préférable; mon scénario spécifique est principalement la motivation du problème.
einpoklum

Réponses:

16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

Appelez ensuite range1au lieu de strf::range.

range1_ptr<T>(...)peut toujours être utilisé pour appeler explicitement le modèle en prenant un argument de modèle, mais ne fait aucune déduction à partir des arguments. range1réplique la déduction du strf::rangemodèle d' origine .

Cela fonctionne, car [temp.deduct.funcaddr] / 1 indique que la déduction d'argument de modèle lors de la prise de l'adresse d'une fonction sans type cible de la conversion est effectuée sur chaque modèle de fonction candidat comme si les listes de paramètres et d'arguments d'un appel hypothétique étaient vide. Le deuxième argument de modèle ne peut donc pas être déduit pour la deuxième surcharge avec deux paramètres de modèle. Le seul candidat restant est la première surcharge, qui sera choisie comme cible du pointeur de fonction.

Tant qu'il n'y a pas de deuxième modèle de fonction candidat pour lequel un modèle-id valide avec un seul argument peut être formé, range1_ptrpeut toujours être utilisé pour appeler le modèle de fonction en prenant un argument sans ambiguïté. Sinon, l'instanciation de range1_ptrdonnera une erreur en raison de l'ambiguïté.

noyer
la source
N'y aura-t-il pas d'ambiguïté strf::range<T>?
einpoklum
1
@einpoklum Il compile très bien sur GCC et Clang. Je n'ai pas vérifié la norme, mais je serais surpris si cela devait être ambigu.
noyer
Vous devriez peut-être changer le nom de la fonction en pretty_please_with_sugar_on_top()? ... C ++ peut être si bizarre parfois ...
einpoklum
Vous avez réussi à déloger la réponse acceptée :-P
einpoklum
11

Et si vous passiez par un using?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);
max66
la source
Et cela compile? La deuxième ligne semble particulièrement suspecte.
einpoklum
@einpoklum - drôle, n'est-ce pas?
max66
@einpoklum - n'est malheureusement pas une solution générale; fonctionne dans ce cas car (si je ne me trompe pas) seule la première range()version est compatible avec tpf; l'autre cas peut être différent.
max66
@einpoklum - dans la deuxième ligne, vous pouvez également expliquer le paramètre du modèle ( tfp x = &strf::range<char const *>;); de cette façon, je suppose que vous avez une solution générale, presque équivalente à celle du noyer
max66
0

Une solution est

1) tout d'abord, vous devez spécifier le type du deuxième argument, par exemple (char *)(some_char_ptr + some_length)

2) ne pas utiliser constpour les deux, cela fonctionne bien:

strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

Vous pouvez essayer de le remplacer (char *)par (const char *)à gauche OU à droite, cela fonctionne toujours.

tontonCD
la source
C'est assez moche si les arguments pointent vers des constdonnées.
aschepler
1. Il n'y a pas de deuxième argument. Je veux le modèle à argument unique. 2. Hack intéressant! -1 pour la première suggestion et +1 pour la seconde :-P
einpoklum