2016-10-31 26 views

Odpowiedz

24
  1. Jeśli udostępniasz C API z implementacją C++, szablony nie są opcją dla interfejsu API. Varargs są.

  2. Jeśli potrzebujesz obsługi kompilatora, który nie obsługuje C++ 11 lub nowszego standardu, szablony variadic nie są dostępne. Varargs są.

  3. Jeśli potrzebujesz firewalla kompilacyjnego. To znaczy. musisz ukryć implementację funkcji z nagłówka, wtedy szablon variadic nie jest opcją. Varargs są.

  4. W systemach z ograniczoną pamięcią (osadzonych) różne funkcje generowane przez szablon mogą wprowadzać zbyt duże rozrosty. Powiedział, że takie systemy są zwykle również w czasie rzeczywistym, w którym to przypadku varargs może być również niedopuszczalne ze względu na rozgałęzienia i użycie stosu.

+10

varargs są również używane w niektórych metaprogrammingach (cechy wprowadzane na przykład w SFINAE) z powodu ich właściwości, która jest uważana za ostatnią na rozdzielczość przeciążania. – bolov

+0

@bolov fajne! Nigdy nie spotkałem się z tym przypadkiem użycia. Brzmi też * zbyt * sprytnie dla mojego dobra :) – user2079303

+0

@bolov czy mógłbyś podać więcej szczegółów, nawet przykład kodu lub link –

14

chcę dodać do excellent answer of @user2079303

varargs wykorzystywane są również w pewnym metaprogramowanie (cech realizowanego z SFINAE na przykład), ze względu na ich właściwości są uważane za ostatni od rozdzielczości przeciążenia.

Powiedzmy chcemy wdrożyć cechę, aby wykryć, czy klasa jest constructible od niektórych rodzajów (coś jak std::is_constructible:

Uproszczony nowoczesna realizacja pójdzie tak (to nie jedyny sposób: jak wspomniano, void_t może również be used to implement the trait i wkrótce (? 2020) nie będziemy potrzebować każdy z tych sztuczek jak concepts są w drodze z klauzulą ​​require jako obywatela pierwszej klasy):

template <class T, class... Args> struct Is_constructible { 

    template <class... Params> 
    static auto test(Params... params) -> decltype(T{params...}, std::true_type{}); 

    static auto test(...) -> std::false_type; 

    static constexpr bool value = decltype(test(std::declval<Args>()...))::value; 
}; 

To działa, ponieważ od SFINAE: kiedy instantiati ng test, jeśli semantyka jest nieważna z powodu jakiegoś dependent name, to nie jest to trudny błąd, zamiast tego przeciążenie jest po prostu ignorowane.

Jeśli chcesz dowiedzieć się więcej o sztuczkach rysunkowych i ich zastosowaniu oraz o tym, jak działają, możesz przeczytać dalej: sfinae idiom, member detector idiom, enable_if idiom.

Więc z typem X, które mogą być wykonane z zaledwie 2 wskazówki:

struct X { X(int, int) {}; }; 

możemy uzyskać te wyniki:

Is_constructible<X, int, int>::value // true 
Is_constructible<X, int>::value;  // false 

Teraz powstaje pytanie, czy możemy zastąpić test varargs z szablony variadic:

template <class T, class... Args> struct Is_constructible_broken { 

    template <class... Params> 
    static auto test(Params... params) -> decltype(T{params...}, std::true_type{}); 

    template <class... Params> 
    static auto test(Params...) -> std::false_type; 

    static constexpr bool value = decltype(test(std::declval<Args>()...))::value; 
}; 

Odpowiedź brzmi nie (przynajmniej nie t bezpośrednia wymiana).Kiedy instancji

Is_constructible_broken<X, int, int>::value 

otrzymujemy błąd:

error: call of overloaded ' test(int, int) ' is ambiguous

ponieważ oba przeciążenia są opłacalne i oba mają taką samą rangę „” w rozdzielczości przeciążenia. Pierwsza implementacja z varargs działa, ponieważ nawet jeśli oba przeciążenia są wykonalne, szablon wariadyczny jeden jest preferowany w porównaniu z wariantem vararg.

Okazuje się, że faktycznie można to zrobić przy użyciu szablonów variadic. Sztuką jest, aby dodać sztuczną parametr test to jest idealne dopasowanie dla pierwszego przeciążenia i konwersja do drugiego:

struct overload_low_priority{}; 
struct overload_high_priority : overload_low_priority {}; 

template <class T, class... Args> struct Is_constructible2 { 

    template <class... Params> 
    static auto test(overload_high_priority, Params... params) 
     -> decltype(T{params...}, std::true_type{}); 

    template <class... Params> 
    static auto test(overload_low_priority, Params...) -> std::false_type; 

    static constexpr bool value 
     = decltype(test(overload_high_priority{}, std::declval<Args>()...))::value; 
}; 

Ale myślę, że varargs jest bardziej jasne w tej sprawie.

+0

Zauważ, że 'void_t' może być użyte do stworzenia cech zamiast użycia elipsy. – Jarod42

+0

@ Jarod42 tak, a wkrótce będzie to również przestarzałe przez [wymóg klauzuli] (http://en.cppreference.com/w/cpp/language/constraints) – bolov

+0

Preferuję użycie 'struct high_overload_priority: low_overload_priority {};' ponad 'int' /' long', aby zamówić przeciążenie. – Jarod42

3

vararg umożliwia używanie __attribute__ format. Na przykład.

void debug(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 

void f(float value) 
{ 
    debug("value = %d\n", value); // <- will show warning. 
} 

Niestety nie można tego osiągnąć przy użyciu szablonów variadic.

Edytowane: Jak podkreślił Vladimir, zapomniałem wspomnieć, że __attribute__ format nie jest częścią standardu, jednak obsługuje go zarówno GCC, jak i Clang (ale nie Visual Studio). Aby uzyskać więcej informacji, zobacz: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

+0

Sugerowałbym, żebyś wstawił link użycia formatu __attribute__, ponieważ nie jest w standardzie. Uważam też, że dobrze jest pokazać niektóre z kompilatorów, których używa. Znam tylko GCC. –

+0

https://gc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html –

Powiązane problemy