宣言した関数のシグネチャを調べることにより、2つの宣言を明確にすることができます。パラメータタイプを検査するために必要なテンプレートの基本的な例を次に示します。これは簡単に一般化できます(または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;
}
あなたは、パラメータの種類の資格を検出することができたら、コールに、2つのラッパー関数を書くことができるiconv
1呼び出している:iconv
とchar const**
引数と1通話ことiconv
でchar**
引数。
関数テンプレートの特殊化は避ける必要があるため、クラステンプレートを使用して特殊化を行います。また、使用する特殊化のみが確実にインスタンス化されるように、各呼び出し元を関数テンプレートにします。コンパイラが誤った特殊化用のコードを生成しようとすると、エラーが発生します。
次に、これらの使用法をaでラップして、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 */);
}
(この後者のロジックは、クリーンアップして一般化することができます。うまくいけば、それがどのように機能するかを明確にするために、その各部分を明示的にしようとしました。)