¿Cómo puedo evitar que C ++ adivine un segundo argumento de plantilla?

26

Estoy usando una biblioteca C ++ ( strf ) que, en algún lugar dentro de ella, tiene el siguiente código:

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

Ahora, quiero usar strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)en mi código. Pero si lo hago, me sale el siguiente error (con 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)

El código de la biblioteca probablemente se puede cambiar para evitar esto (por ejemplo, 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)

para asegurarse de Rangeque no sea un puntero); pero no puedo hacer ese cambio en este momento. En cambio, quiero indicar de alguna manera al compilador que realmente realmente quiero tener solo un argumento de plantilla, no uno especificado y otro deducido.

¿Puedo hacer eso?

Agradecería respuestas para C ++ 11 y C ++ 14; Las respuestas de C ++ 17 que involucran guías de deducción son menos relevantes, pero si tiene una, publíquela (para futuras versiones de NVCC ...)


Actualización: la biblioteca strf en sí misma se ha actualizado para sortear esta situación, pero la pregunta se presenta como se le preguntó.

einpoklum
fuente
1
Supongo que pasar un iterador personalizado que envuelve un char*pero no es uno, ¿no es una solución?
Konrad Rudolph
1
@KonradRudolph: Esa es una solución, pero no responde mi pregunta. De hecho, ya tengo otra solución (específica para lo que está en /*...*/), pero me gustaría tomar el camino aquí.
einpoklum
1
En ese caso, mi respuesta (adivinada) es "no se puede hacer", desafortunadamente. Para ser justos, no estoy seguro de aceptar mi solución alternativa sugerida en mi propio código.
Konrad Rudolph
Solo para aclarar: ¿Desea una solución general, que siempre funcione para diferenciar una llamada entre sobrecargas de plantillas con uno o dos parámetros o desea solo una solución específica para este caso?
nogal
@walnut: la solución general sería mejor; mi escenario específico es principalmente la motivación del problema.
einpoklum

Respuestas:

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

Luego llame en range1lugar de strf::range.

range1_ptr<T>(...)siempre se puede usar para llamar explícitamente a la plantilla tomando un argumento de plantilla, pero no hace ninguna deducción de los argumentos. range1replica la deducción de la strf::rangeplantilla original .

Esto funciona porque [temp.deduct.funcaddr] / 1 dice que la deducción de argumentos de plantilla al tomar la dirección de una función sin el tipo de destino de la conversión se realiza en cada plantilla de función candidata como si las listas de parámetros y argumentos de una llamada hipotética fueran vacío. Por lo tanto, el segundo argumento de plantilla no se puede deducir para la segunda sobrecarga con dos parámetros de plantilla. El único candidato que queda es la primera sobrecarga, que se elegirá como el objetivo del puntero de función.

Siempre que no haya una segunda plantilla de función candidata para la cual se pueda formar una ID de plantilla válida con un solo argumento, range1_ptrsiempre se puede usar para llamar a la plantilla de función tomando un argumento sin ambigüedades. De lo contrario, la instanciación de range1_ptrdará un error debido a la ambigüedad.

nuez
fuente
¿No habrá ambigüedad al respecto strf::range<T>?
einpoklum
1
@einpoklum Se compila bien en GCC y Clang. No verifiqué el estándar, pero me sorprendería si se supone que es ambiguo.
nogal
¿Quizás debería cambiar el nombre de la función a pretty_please_with_sugar_on_top()? ... C ++ puede ser tan raro a veces ...
einpoklum
Se las arregló para quitar la respuesta aceptada
:-P
11

¿Qué hay de pasar por un using?

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

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);
max66
fuente
¿Y esto compila? La segunda línea parece particularmente sospechosa.
einpoklum
@einpoklum: divertido, ¿no?
max66
@einpoklum - desafortunadamente no es una solución general; funciona en este caso porque (si no me equivoco) solo la primera range()versión es compatible con tpf; otro caso puede ser diferente.
max66
@einpoklum: en la segunda línea también puede explicar el parámetro de plantilla ( tfp x = &strf::range<char const *>;); de esta manera, supongo que tiene una solución general, casi equivalente a la de la nuez
max66
0

Una solución es

1) en primer lugar, debe especificar el tipo para el segundo argumento, p. Ej. (char *)(some_char_ptr + some_length)

2) no use constpara ambos, esto funciona bien:

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

Puede intentar reemplazar (char *)con (const char *)a la izquierda O a la derecha, todavía funciona.

tontonCD
fuente
Esto es bastante feo si los argumentos apuntan a los constdatos.
Aschepler
1. No hay un segundo argumento. Quiero la plantilla de argumento único. 2. Hack interesante! -1 para la primera sugerencia y +1 para la segunda
:-P