21

たとえば、これはコンパイルに失敗します。

std::function<decltype(printf)> my_printf(printf);

gcc では、エラー メッセージは次のようになります。

error: variable 'std::function<int(const char*, ...)> my_printf' has initializer but incomplete type
     std::function<decltype(printf)> my_printf(printf);

最初はこれは gcc のバグだと思っていましたが、標準を調べたところ、これはサポートされていないようです。これの技術的な理由は何ですか?

4

2 に答える 2

8

問題は実装の 1 つです。可能だったとしましょう。次にstd::function宣言する必要があります(printfの場合)

int operator()(char* fmt, ...)

呼び出されると、割り当てたオブジェクトに ... の内容を渡す必要があります。問題は、それを伝える方法を知るために引数について十分に知らないことです。これは問題です。printf() はフォーマットを解析しますが、他のメカニズムを使用するものもあります ('end' 値が一般的です)。

関数の printf ファミリについては、vXXX バージョン (vprintf など) を参照することをお勧めします。std::functionそれらは明確に定義された引数 (最後の引数は可変引数リスト) を使用するため、それらのバージョンにバインドすることが可能です。

編集:

ただし、できることは、vprintf関数を使用し、vararg-> va_list 変換を処理する独自のラッパーを作成することです。

#include <cstdio>
#include <cstdarg>
#include <functional>

class PrintWrapper
{

    public:
    PrintWrapper()  = default;


    template<typename T>
    PrintWrapper( T&& t)  : func(std::forward<T>(t))
    {

    }


    int operator()(char const* format, ...)
    {
        va_list args;
        va_start(args, format);
        int result = func(format, args);
        va_end(args);
        return result;
    }
    private:

    std::function< int(char const*, va_list)> func;

};

int main()
{
    // Note, you have to use the 'v' versions
    PrintWrapper p = std::vprintf;
    p("%d %d %s\n", 1,2, "hello");
    char buffer[256];
    using namespace std::placeholders;
    p = std::bind(std::vsnprintf, buffer, sizeof(buffer), _1, _2 );

    p("%lf %s\n", 0.1234, "goodbye");

    // Since the previous step was a wrapper around snprintf, we need to print
    // the buffer it wrote into
    printf("%s\n", buffer);

    return 0;
}

http://ideone.com/Sc95ch

于 2013-08-22T01:55:12.737 に答える