実際、文字列との間の変換で考えられるすべてのケースをカバーするには、かなり精巧なメカニズムが必要です。boost::lexical_cast
以下に可能な実装を貼り付けましたが、代わりに使用したくない理由は確かに疑問です。
//Beware, brain-compiled code ahead!
namespace detail {
template< typename T, typename S >
struct my_lexical_caster {
static T my_lexical_cast(const S& s) {
std::stringstream ss;
if( !(ss << s) ) throw std::bad_cast("cannot stream from source");
T t;
if( !(ss >> t) ) throw std::bad_cast("cannot stream to target");
return t;
}
};
template< typename S >
struct my_lexical_caster<std::string,S> {
static std::string my_lexical_cast(const S& s) {
std::ostringstream oss;
if( !(oss << s) ) throw std::bad_cast("cannot stream from source");
return oss.str();
}
};
template< typename T >
struct my_lexical_caster<T,std::string> {
static T my_lexical_cast(const std::string& s) {
std::stringstream ss(s);
T t;
if( !(ss >> t) ) throw std::bad_cast("cannot stream to target");
return t;
}
};
template< typename T >
struct my_lexical_caster<T,T> {
static const T& my_lexical_cast(const T& s) {return s;}
};
template<>
struct my_lexical_caster<std::string,std::string> {
static const std::string& my_lexical_cast(const std::string& s) {return s;}
};
}
template< typename T, typename S >
inline T my_lexical_cast(const S& s)
{
return detail::my_lexical_caster<T,S>::my_lexical_cast(s);
}
では、なぜこれがとても複雑なのですか?
まず、ここに 2 つのテンプレート パラメーターがあり、そのうちの 1 つが の戻り値の型を決定する方法を確認しますmy_lexical_cast<>()
。ここで、特定の特殊な型に対して特別な実装を提供する必要があります。関数のさまざまな引数に基づいて関数をオーバーロードできますが、戻り値に基づいて関数をオーバーロードすることはできません。したがって、関数テンプレートをオーバーロードする代わりに、テンプレートを特殊化する必要があります。
ただし、これにも問題があります。関数テンプレートの部分的な特殊化はなく、完全な特殊化のみです。これの一般的な理由は、関数テンプレートの部分的な特殊化の代わりに、関数テンプレートのオーバーロードがあることです。そうかもしれませんが、戻り値の型が関係している場合は役に立ちません。
不足している関数テンプレートの部分的な特殊化を回避する一般的な方法は、_class テンプレートの部分的な特殊化を使用することです。代わりに、それが利用可能です。これは、クラス テンプレートを作成し、その public static メンバー関数にアルゴリズムを実装することによって行われます。その後、クラス テンプレートを部分的に特殊化することができ、各特殊化には静的メンバー関数の独自の実装を含めることができます。したがって、これは名前空間
にクラス テンプレート (実際には構造体ですが、唯一のメンバーを明示的に公開する手間を省くためのものです) がある理由を説明しています。detail
そして、なぜこれほど多くの専門分野が必要なのでしょうか?
まず、ストリーム可能な型から別の型に変換する一般的な実装が必要です。
次に、ご覧のとおり、 string に変換するケースをカバーするために 1 つの特殊化が必要です。このケースではデフォルトの実装に問題があるためです。
両方のテンプレート パラメータが同じ型である場合に一致するのは、純粋な最適化です。 を に変換int
したい場合はint
、元の値を渡すだけです。一体なぜそんなことをしたいのかと自問するかもしれませんが、コードがどの型で呼び出されるかわからないテンプレート コードでは、このようなことが常に起こります。
文字列から他の型への変換の特殊化も最適化です。文字列をストリームにストリーミングすることを回避し、代わりに出力文字列ストリームを直接初期化します。これは、後者が実際には前者よりも高速であることを前提としています。これを測定したことはありませんが、遅くなることはないと安全に想定できると思います。
それは最後のものを残し、文字列を文字列に「変換」します。なぜこれが必要なのですか? そのケースは、「変換」とT
への変換で既にカバーされていませんT
か? 意味的には正しいことをするので、これも使用できます。しかし、コンパイラはセマンティクスを気にしません。その唯一の懸念は構文です。std::string
そして、aを aに「変換」したい場合、コンパイラは、部分的にstd::string
一致する 3 つの特殊化を見つけます: 、、および. この場合何をすべきかわからず、エラーをスローするため、これら 3 つのいずれよりも満場一致でより良い一致を提供することで、それを支援する必要があります。<T,std::string>
<std::string,T
<T,T>