7
#include <functional>

void f1(int)
{}

void f2(int, ...)
{}

int main()
{
    std::function<void(int)>      g1 = f1; // OK.
    std::function<void(int, ...)> g2 = f2; // Error! Not standard C++!
}

C++11 が次のような特殊なテンプレート クラスを提供しないのはなぜですか。

template<class ResultType, class... ArgTypes>
class function<ResultType(ArgTypes......)>
{
    // ... ... ...
};
4

1 に答える 1

5

その専門分野が提供されない最終的な理由を提供するつもりはありませんが(私はそれを知りません)、それを実装しようとするときに遭遇する可能性のある技術的な障害のいくつかを示唆することができます。それはうまくいけば、専門分野がそこにない理由の感覚をあなたに与えるでしょう。

std::function<>まず、クラステンプレート自体をどのように実装できるかを考えてみましょう。その設計の根底にある型消去手法は、次のようにスケッチできます(これは単なる例示的な単純化であり、実際の実装ははるかに複雑です)。

#include <memory>

template<typename T>
struct function { };

template<typename R, typename... Args>
struct function<R(Args...)>
{

public:

    template<typename F>
    function(F&& f) : _holder(
        new holder<typename std::decay<F>::type>(std::forward<F>(f))
        )
    { }

    R operator () (Args&&... args)
    { _holder->call(std::forward<Args>(args)...); }

private:

    struct holder_base
    { virtual R call(Args&&... args) = 0; };

    template<typename F>
    struct holder : holder_base
    {
        holder(F&& f) : _f(std::forward<F>(f)) { }
        R call(Args&&... args) { return _f(std::forward<Args>(args)...); }
        F _f;
    };

    std::unique_ptr<holder_base> _holder;
};

次に、楕円の特殊化がどのようになるかを見てみましょう。まず第一に、可変個引数関数に提供される引数の数とタイプは、その関数のシグネチャで固定されていません。したがって、専用テンプレートの呼び出し演算子は、任意の数とタイプの引数を受け入れる関数テンプレートである必要があります。

template<typename R, typename... Args>
struct function<R(Args.......)>
{
    ...

    template<typename... Ts>
    R operator () (Args&&... args, Ts&&... ts)
    { _holder->call(std::forward<Args>(args)..., std::forward<Ts>(ts)...); }

    ...

これにより、holder<>呼び出し演算子を可変個引数関数テンプレートにする必要があります。ただし、型消去を実現するには、同じ呼び出し演算子がである必要があり、関数テンプレートをC++にvirtualすることはできません。virtual

可変個引数のテンプレートパラメータと完全な転送を繰り返す必要なしに、可変個引数の引数(ここでは楕円について話している)を簡単に転送できれば、物事は確かに簡単になります。ただし、それを実現する簡単な方法はわかりません。特に、可変個引数リストに一致する引数以外の引数が関数に渡されない場合はそうではありません。

于 2013-02-19T02:51:07.673 に答える