Come posso impedire a C ++ di indovinare un secondo argomento modello?


26

Sto usando una libreria C ++ ( strf ) che, da qualche parte al suo interno, ha il seguente codice:

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) { /* ... */ }
}

Ora voglio usare strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)nel mio codice. Ma se lo faccio, ottengo il seguente errore (con NVCC di 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)

Il codice della libreria può probabilmente essere modificato per evitarlo (ad es. Usando:

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)

assicurarsi che Rangenon sia un puntatore); ma non posso apportare quel cambiamento in questo momento. Invece, voglio in qualche modo indicare al compilatore che intendo davvero avere solo un argomento template, non uno specificato e un altro dedotto.

Posso farlo?

Gradirei le risposte per C ++ 11 e C ++ 14; Le risposte C ++ 17 che coinvolgono le guide alla detrazione sono meno rilevanti, ma se ne hai una, per favore pubblicala (per future versioni NVCC ...)


Aggiornamento: la stessa libreria strf è stata aggiornata per aggirare questa situazione, ma la domanda si pone come posta.


1
Immagino che passare un iteratore personalizzato che avvolge sottilmente un char*ma non è uno non è una soluzione?
Konrad Rudolph,

1
@KonradRudolph: Questa è una soluzione alternativa, ma non risponde alla mia domanda. In realtà ho già un'altra soluzione (specifica per ciò che è in /*...*/), ma mi piacerebbe prendere la strada maestra qui.
einpoklum,

1
In tal caso, purtroppo la mia risposta (indovinata) è "impossibile". Ad essere sinceri, non sono sicuro che accetterei la soluzione suggerita nel mio codice.
Konrad Rudolph,

Solo per chiarire: vuoi una soluzione generale, che funzionerebbe sempre per differenziare una chiamata tra sovraccarichi di template con uno contro due parametri o vuoi solo una soluzione specifica per questo caso?
noce

@walnut: la soluzione generale sarebbe migliore; il mio scenario specifico è principalmente la motivazione del problema.
einpoklum,

Risposte:


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

Quindi chiama range1invece di strf::range.

range1_ptr<T>(...)può sempre essere usato per chiamare esplicitamente il modello prendendo un argomento template, ma non fa alcuna deduzione dagli argomenti. range1replica la detrazione dal strf::rangemodello originale .

Questo funziona perché [temp.deduct.funcaddr] / 1 afferma che la deduzione dell'argomento modello quando si prende l'indirizzo di una funzione senza tipo di destinazione della conversione viene eseguita su ciascun modello di funzione candidato come se gli elenchi di parametri e argomenti di una chiamata ipotetica fossero vuoto. Pertanto, il secondo argomento del modello non può essere dedotto per il secondo sovraccarico con due parametri del modello. L'unico candidato rimasto è il primo sovraccarico, che verrà scelto come target del puntatore alla funzione.

Finché non esiste un secondo modello di funzione candidato per il quale può essere formato un ID modello valido con un solo argomento, range1_ptrpuò sempre essere utilizzato per chiamare il modello di funzione prendendo un argomento in modo inequivocabile. Altrimenti, l'istanza di range1_ptrdarà un errore a causa dell'ambiguità.


Non ci sarà ambiguità in merito strf::range<T>?
einpoklum,

1
@einpoklum Si compila bene su GCC e Clang. Non ho controllato lo standard, ma sarei sorpreso se ciò dovesse essere ambiguo.
noce

Forse dovresti cambiare il nome della funzione in pretty_please_with_sugar_on_top()? ... Il C ++ può essere così strano a volte ...
einpoklum il

Sei riuscito a annullare la risposta accettata :-P
einpoklum

11

Che ne dici di passare attraverso un using?

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

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

E questo compila? La seconda riga sembra particolarmente sospetta.
einpoklum,

@einpoklum - divertente, vero?
max66,

@einpoklum - purtroppo non è una soluzione generale; funziona in questo caso perché (se non sbaglio) range()è compatibile solo la prima versione tpf; l'altro caso può essere diverso.
max66,

@einpoklum - nella seconda riga puoi anche spiegare il parametro template ( tfp x = &strf::range<char const *>;); in questo modo suppongo che tu abbia una soluzione generale, quasi equivalente a quella della noce
max66

0

Una soluzione è

1) prima di tutto, dovresti specificare il tipo per il secondo argomento, ad es (char *)(some_char_ptr + some_length)

2) non usare constper entrambi, funziona bene:

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

Puoi provare a sostituirlo (char *)con (const char *)a sinistra O a destra, funziona ancora.


Questo è piuttosto brutto se gli argomenti indicano i constdati.
aschepler

1. Non c'è un secondo argomento. Voglio il modello a argomento singolo. 2. Hack interessante! -1 per il primo suggerimento e +1 per il secondo :-P
einpoklum
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.