17

Mam funkcję, która przyjmuje jeden parametr z wartością domyślną. Teraz chcę również, aby pobierał zmienną liczbę parametrów i przekazywał je do innej funkcji. Parametry funkcji z wartością domyślną muszą być ostatnie, więc ... czy mogę umieścić ten parametr po pakiecie variadic, a kompilator wykryje, czy go dostarczam, czy nie, podczas wywoływania funkcji?C++ parametr funkcji szablonu variadic z wartością domyślną

(Zakładając, że pakiet nie zawiera typu tego ostatniego parametru, w razie potrzeby możemy założyć, że ten typ ogólnie nie powinien być znany użytkownikowi, w przeciwnym razie jest uważany za niewłaściwe użycie mojego interfejs mimo to ....)

template <class... Args> 
void func (Args&&... args, SomeSpecialType num = fromNum(5)) 
{ 
} 

Odpowiedz

16

Nie, paczki muszą być ostatnie.

Ale możesz go sfałszować. Możesz wykryć, jaki jest ostatni typ w paczce. Jeśli jest to SomeSpecialType, możesz uruchomić swój func. Jeśli nie jest to SomeSpecialType, możesz rekurencyjnie wywoływać się z przekazanymi argumentami i dołączonymi fromNum(5).

Jeśli chcesz mieć ochotę, to sprawdzenie może zostać wykonane w czasie kompilacji (tj. Innego przeciążenia) przy użyciu technik SFINAE. Ale to prawdopodobnie nie jest warte kłopotów, biorąc pod uwagę, że kontrola "run-time" będzie stała na danym przeciążeniu, a zatem prawie na pewno zostanie zoptymalizowana, a SFINAE nie powinno być używane lekko.

To nie daje podpisu, który chcesz, ale daje ci to, czego chcesz. Będziesz musiał wyjaśnić zamierzony podpis w komentarzach.

Coś takiego, po usunięciu literówki i podobne:

// extract the last type in a pack. The last type in a pack with no elements is 
// not a type: 
template<typename... Ts> 
struct last_type {}; 
template<typename T0> 
struct last_type<T0> { 
    typedef T0 type; 
}; 
template<typename T0, typename T1, typename... Ts> 
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...> {}; 

// using aliases, because typename spam sucks: 
template<typename Ts...> 
using LastType = typename last_type<Ts...>::type; 
template<bool b, typename T=void> 
using EnableIf = typename std::enable_if<b, T>::type; 
template<typename T> 
using Decay = typename std::decay<T>::type; 

// the case where the last argument is SomeSpecialType: 
template< 
    typename... Args, 
    typename=EnableIf< 
    std::is_same< 
     Decay<LastType<Args...>>, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    // code 
} 

// the case where there is no SomeSpecialType last:  
template< 
    typename... Args, 
    typename=EnableIf< 
    !std::is_same< 
     typename std::decay<LastType<Args...>>::type, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    func(std::forward<Args>(args)..., std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

// the 0-arg case, because both of the above require that there be an actual 
// last type: 
void func() { 
    func(std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

lub coś znacznie takiego.

+0

To jest jak obejście, to inna sygnatura, ale to samo zachowanie ... Rozumiem. Właściwie planowałem usunąć ten parametr w przyszłości, więc może nie jest to warte wysiłku (a podpis byłby mylący). Czy możesz pokazać mi prosty przykład? – cfa45ca55111016ee9269f0a52e771

+0

@ fr33domlover Naszkicowałem projekt. Nie zostały skompilowane, nie mówiąc już debugowane, ale powinny być tam podstawy. – Yakk

+0

Dzięki, spróbuję, jeśli nie zdecyduję się usunąć pojedynczego parametru. Wygląda na skomplikowaną, a podpis nie jest przechowywany, więc może nie być warta problemów ... w każdym razie dzięki – cfa45ca55111016ee9269f0a52e771

3

Innym podejściem byłoby przekazywanie argumentów variadycznych przez krotkę.

template <class... Args> 
void func (std::tuple<Args...> t, SomeSpecialType num = fromNum(5)) 
{ 
    // don't forget to move t when you use it for the last time 
} 

Zalety: interfejs jest znacznie prostszy, przeciążanie i dodawanie domyślnych argumentów jest dość łatwe.

Minusy: wywołujący musi ręcznie zawijać argumenty w rozmowie std::make_tuple lub std::forward_as_tuple. Prawdopodobnie będziesz musiał użyć sztuczek std::index_sequence, aby zaimplementować tę funkcję.

Powiązane problemy