Puede eliminar la ambigüedad entre las dos declaraciones inspeccionando la firma de la función declarada. A continuación, se muestra un ejemplo básico de las plantillas necesarias para inspeccionar el tipo de parámetro. Esto podría generalizarse fácilmente (o podría usar los rasgos de función de Boost), pero esto es suficiente para demostrar una solución para su problema específico:
#include <iostream>
#include <stddef.h>
#include <type_traits>
// I've declared this just so the example is portable:
struct iconv_t { };
// use_const<decltype(&iconv)>::value will be 'true' if the function is
// declared as taking a char const**, otherwise ::value will be false.
template <typename>
struct use_const;
template <>
struct use_const<size_t(*)(iconv_t, char**, size_t*, char**, size_t*)>
{
enum { value = false };
};
template <>
struct use_const<size_t(*)(iconv_t, char const**, size_t*, char**, size_t*)>
{
enum { value = true };
};
Aquí hay un ejemplo que demuestra el comportamiento:
size_t iconv(iconv_t, char**, size_t*, char**, size_t*);
size_t iconv_const(iconv_t, char const**, size_t*, char**, size_t*);
int main()
{
using std::cout;
using std::endl;
cout << "iconv: " << use_const<decltype(&iconv) >::value << endl;
cout << "iconv_const: " << use_const<decltype(&iconv_const)>::value << endl;
}
Una vez que pueda detectar la calificación del tipo de parámetro, puede escribir dos funciones contenedoras que llaman iconv
: una que llama iconv
con un char const**
argumento y otra que llama iconv
con un char**
argumento.
Debido a que debe evitarse la especialización de la plantilla de función, usamos una plantilla de clase para realizar la especialización. Tenga en cuenta que también hacemos de cada uno de los invocadores una plantilla de función, para asegurarnos de que solo se instancia la especialización que usamos. Si el compilador intenta generar código para la especialización incorrecta, obtendrá errores.
Luego ajustamos el uso de estos con un call_iconv
para que llamar a esto sea tan simple como llamar iconv
directamente. El siguiente es un patrón general que muestra cómo se puede escribir esto:
template <bool UseConst>
struct iconv_invoker
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
template <>
struct iconv_invoker<true>
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
size_t call_iconv(/* arguments */)
{
return iconv_invoker<
use_const<decltype(&iconv)>::value
>::invoke(&iconv, /* arguments */);
}
(Esta última lógica podría limpiarse y generalizarse; he intentado hacer que cada parte de ella sea explícita para, con suerte, dejar más claro cómo funciona).