2017-12-29 9 views
0

私はprintfに転送される前に書式設定引数をフィルタリングするための "サニタイザー"関数を構築しようとしています。テンプレートの特殊化と完璧な転送

template<typename A> 
_CR_INLINE decltype(auto) sanitize_forward(A&& arg) { 
    return std::forward<A>(arg); 
} 

template<> 
_CR_INLINE decltype(auto) sanitize_forward<std::string>(std::string&& arg) { 
    return std::forward<const char*>(arg.c_str()); 
} 

だから、すべてのstd::stringが適切にフォーマットされるためには、const char*に「崩壊」になっています。

template<typename...Args> 
_CR_INLINE void printf_safe(const std::string& format, Args&&...args) { 
    std::printf(format.c_str(), sanitize_forward<Args>(args)...); 
} 

私は引数が、私はstd::forwardを返す理由である完全printfから転送、なりたいです。しかし、私は実際にこれをどのように実装すべきかについて頭を落とすことはできません。

1)decltype(auto)は正しいですか? std::forwardの戻り値のr値参照を保持する必要があります。

2)テンプレートを特化するにはどうすればいいですか:std::string&&またはstd::string

3)推薦転送の参照は実際のタイプと同じである必要があります。

+1

あなたは 'printf'に進む必要はありません。 –

+0

しかし、printfが内部ログ機能であると仮定しましょう。これはどのように実装する必要がありますか?また、必要でない引数を正確に転送する理由は何ですか? – mutex36

答えて

2

したがって、書式設定の引数がprintfに転送される前にフィルターをかけるために、「サニタイザー」関数を構築しようとしています。

あなたの前提が間違っているようです。 printfは、va_argを使用するC関数です。完璧な転送は必要ありません。

http://en.cppreference.com/w/cpp/io/c/fprintf

また、その引数を「消費」するつもりはありませんが、単にそれらから読み込まれます。一時的なものと非一時的なものを区別するのは意味がありません。printfラッパーにconst Args&...と書いてください。


1)decltype(auto)は正しいですか? std::forwardの戻り値のr値参照を保持する必要があります。

std::forward左辺値参照または右辺値参照を返しますどちらか。 decltype(auto)はそれを確かに保存します。

sanitize_forwardstd::stringの特殊化は役に立ちません。std::forward<const char*>は常にconst char* &&を返します。私はそれが異なるだとは思わない:

template<> 
_CR_INLINE const char* sanitize_forward<std::string>(std::string&& arg) { 
    return arg.c_str(); 
} 

また、std::string右辺値参照ため.c_str()を返すことは非常に危険と間違って聞こえる:あなたは程度だ文字列の内部バッファへのポインタを取っています期限が切れる。ここではconst std::string&とします。


2)どのように私は私のテンプレートを専門にする必要がありますstd::string&&またはstd::string

どのように呼び出されますか?テンプレート引数を明示的に指定していますか?テンプレート引数は常に非参照になるか、の左辺の参照の非参照の両方になりますか?あなたはsanitize_forward<Args>を持っているので、あなたはおそらく両方を起動しようとします

...

  • sanitize_forward<std::string&>

    • sanitize_forward<std::string>

    • ...多分 CV-修飾子と。 「専門化」ビジネスを扱う追加の明示的な std::decay_t<Args>パラメーターを指定することもできます。


      3)推定される転送参照は右、実際のタイプと同じでなければなりませんか?

      あなたはこれが何を意味するのかよく分かりません。あなたは詳しく説明できますか?

    3

    転送参照がほとんど完全に間違っています。予想されるものであり、混乱している。

    std::forward自体を参照して、参照縮退ルール、T&&を推測する際のテンプレート引数の減算ルール、およびそれらがどの程度互いに結びついているかを理解する必要があります。

    まず、参照が折りたたまれます。このチャートを見てください:

    If  Then  Then 
    X is  X& is  X&& is 
    T  T&  T&& 
    T&  T&  T& 
    T const& T const& T const& 
    T&&  T&  T&& 
    

    Xintであれば、その後、X&&int&&です。 Xint&の場合、X&&int&です。

    それを再読み込みします。再び。もう1回。両方が適用された場合、&&&に勝ちます。

    次に、控除。私はここに嘘を言うでしょうが、彼らは嘘を単純化しています。

    あなたがX&&を推定テンプレートにFoo&を渡すと、XFoo&あるとX&&Foo&です。

    あなたがX&&を推定テンプレートにFoo&&を渡すと、XFooあるとX&&Foo&&です。

    次に、std::forwardは条件付き移動です。参照変数X&& xの場合、std::forward<X>(x)decltype(x)(x)です。xは、それが宣言された型にキャストします。 xが値の参照である場合、xを値の参照にキャストします。これは、式xのタイプが、xが正の値であっても、X&ではなく、X&&であるために必要です。左辺値の参照は右辺値の参照値ですが、それ自体は右値ではありません。

    コードを修正しました。


    template<class T> 
    struct tag_t{constexpr tag_t(){}}; 
    template<class T> 
    constexpr tag_t<std::decay_t<T>> tag{}; 
    
    template<class T> 
    auto sanitizer(tag_t<T>){ 
        return [](auto&& t)->decltype(auto){ 
        return decltype(t)(t); 
        }; 
    } 
    template<class A> 
    decltype(auto) sanitize(A&& arg) { 
        return sanitizer(tag<A>)(std::forward<A>(arg)); 
    } 
    
    auto sanitizer(tag_t<std::string>) { 
        return [](std::string const& s){return s.c_str();}; 
    } 
    
    template<class...Ts> 
    void printf_safe(const std::string& format, Ts&&...ts) { 
        std::printf(format.c_str(), sanitize(std::forward<Ts>(ts))...); 
    } 
    

    私は、SRP(単一責任の原則)に従う - 私はサニタイズから前方に分割します。

    そして、それを実際に行ってから消毒作用(sanitizer)を分けました(sanitize)。

    これは、完璧な転送が「偶然」で勝利することなく、std::stringのコード化コードを1回書くことができます。

    脇に、配列の引数を非ポインタとして扱う場合は、decayをremove refと置き換えてcvを削除します。

    あなたはtag_tの名前空間のいずれか、あるいは我々は(当然stdにビットをしない)消毒されているタイプの名前空間にsanitizer過負荷を書き込むことによってsanitize機能を拡張することができます。

    関連する問題