2013-02-19 5 views
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++! 
} 

Dlaczego C++ 11 nie zapewnia wyspecjalizowaną klasę szablonu następująco:Dlaczego C++ 11 nie obsługuje 'std :: function <void (int, ...)>?

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

Czy testowałeś szablon, który wygląda tak, aby sprawdzić, czy zadziała? – Omnifarious

+1

Oba fragmenty pokazują zupełnie inne rzeczy. Szablony Variadic są obsługiwane w czasie kompilacji. Argumenty zmiennych stylu "C" są obsługiwane w środowisku wykonawczym. – mfontanini

+0

Składnia parametru '...' (nie w szablonie variadic) jest zachowywana dla kompatybilności wstecznej z C. Jego użycie jest wysoce odradzane. –

Odpowiedz

5

Nie chcę, aby zapewnić ostateczny powód, że specjalizacja nie jest przewidziany (nie wiem tego), ale może mógłbym wskazać na niektóre techniczne przeszkody, które można napotkać, próbując je wdrożyć. Miejmy nadzieję, że da ci poczucie, dlaczego specjalizacji tam nie ma.

Zastanówmy się najpierw, w jaki sposób można zaimplementować szablon klasy std::function<>. Technika typ wymazywania, które leży u podstaw jego projekt może zostać zarysowane w następujący sposób (to jest tylko przykładowy uproszczenie, rzeczywista realizacja jest bardziej skomplikowane):

#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; 
}; 

Teraz zobaczmy jak specjalizacja dla elipsy będzie wyglądać . Przede wszystkim liczba i typ argumentów podanych funkcji wariancji to , a nie ustalony w sygnaturze tej funkcji. W związku z tym operator call naszego specjalistycznego szablonu musi być szablon funkcja przyjmując dowolną liczbę i rodzaj argumentów:

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)...); } 

    ... 

To zmusza nas, z kolei, aby holder<> „s operator call o zmiennej liczbie argumentów szablonu funkcji. Jednak w celu realizacji typu wymazania ten sam operator wywołania musi być virtual, a szablony funkcji nie mogą być w języku C++ wartościami virtual.

Wszystko byłoby z pewnością łatwiejsze, jeśli argumenty variadyczne (mówię tutaj o elipsach) mogą być łatwo przekazane bez konieczności powtarzania parametrów szablonu variadic i perfekcyjnego przekazywania. Nie jestem jednak świadomy prostego sposobu, aby to osiągnąć, a zwłaszcza nie, jeśli żaden inny argument nie zostanie przekazany do funkcji niż te pasujące do listy variadic.

+0

Sądzę, że istnieje kilka innych podejść, które można podjąć. Ale tak, techniczne wyzwania związane z jego wdrożeniem powodują ból w mojej głowie i nie jestem pewien, czy można to zrobić w czysty sposób. – Omnifarious

+0

Ty * możesz * spróbować przekazać obiekt 'va_args' do wirtualnego' operator() (Args ..., va_args vargs) ', a następnie spróbować jakoś rozszerzyć to w' owner''' operator() ' ...ale myślę, że byłby to ogromny bałagan. – Xeo

Powiązane problemy