Ви можете розрізнити між двома оголошеннями, перевіривши підпис заявленої функції. Ось базовий приклад шаблонів, необхідних для перевірки типу параметра. Це можна легко узагальнити (або ви можете скористатися функціональними рисами Boost), але цього достатньо, щоб продемонструвати рішення для вашої конкретної проблеми:
#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 };
};
Ось приклад, який демонструє поведінку:
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;
}
Як тільки ви зможете виявити кваліфікацію типу параметра, ви можете написати дві функції-обгортки, які викликають iconv
: одну, яка викликає iconv
з char const**
аргументом, і одну, яка викликає iconv
з char**
аргументом.
Оскільки слід уникати спеціалізації шаблону функцій, ми використовуємо шаблон класу для спеціалізації. Зверніть увагу, що ми також робимо кожен із засобів, що викликають, шаблоном функції, щоб забезпечити інстанціювання лише тієї спеціалізації, яку ми використовуємо. Якщо компілятор намагається сформувати код для неправильної спеціалізації, ви отримаєте помилки.
Потім ми обертаємо їх використання, call_iconv
щоб зробити так просто, як дзвінок iconv
безпосередньо. Далі наведено загальну схему, яка показує, як це можна написати:
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 */);
}
(Цю останню логіку можна було б очистити та узагальнити; я намагався зробити кожен її фрагмент явним, щоб, сподіваюся, було зрозуміліше, як це працює.)