2

ここSOで、以下のコードのこの素敵な小さなスニペットを見たことがあります。

template<typename to_t, typename from_t>
to_t lex_cast(const from_t &arg) {
    to_t ret;
    std::stringstream os;
    os << arg;
    os >> ret;
    return ret;
}

これはboost::lexical_castを模倣しています。使用法:

string tmp = ::lex_cast<string>(3.14);

ただし、フォーマットされたストリームのデフォルトのスキップにより、以下は期待どおりに機能しません。

string tmp = ::lex_cast<string>("foo bar"); // will only return foo, but I want entire string

(\n についても同様の問題が予想されます)。noskipws とテンプレートの特殊化を設定しようとしましたが、役に立ちませんでした。お知らせ下さい。

4

1 に答える 1

3

実際、文字列との間の変換で考えられるすべてのケースをカバーするには、かなり精巧なメカニズムが必要です。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>

于 2010-10-30T09:41:10.467 に答える