C++11 zmienna liczba argumentów, ten sam specyficzny typ

Pytanie jest proste, jak zaimplementowałbym funkcję przyjmującą zmienną liczbę argumentów (podobnie jak szablon zmienny), jednak gdzie wszystkie argumenty mają ten sam typ, powiedzmy int.

Myślałem o czymś podobnym to;

void func(int... Arguments)

Alternatywnie czy rekurencyjne twierdzenie statyczne na typach działa?

Author: R. Martinho Fernandes, 2013-08-02

6 answers

Możliwym rozwiązaniem jest uczynienie parametru typu kontenerem, który może być inicjowany przez Listę inicjalizatorów brace, np. std::initializer_list<int> lub std::vector<int>. na przykład :

#include <iostream>
#include <initializer_list>

void func(std::initializer_list<int> a_args)
{
    for (auto i: a_args) std::cout << i << '\n';
}

int main()
{
    func({4, 7});
    func({4, 7, 12, 14});
}
 41
Author: hmjd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-02 13:04:08

Oto wersja, która usuwa funkcję z zestawu przeciążeń, zamiast dawać static_assert. To pozwala na zapewnienie innych przeciążeń funkcji, które mogą być używane, gdy typy nie są takie same, zamiast fatalnego static_assert, którego nie można uniknąć.

#include <type_traits>

template<typename... T>
  struct all_same : std::false_type { };

template<>
  struct all_same<> : std::true_type { };

template<typename T>
  struct all_same<T> : std::true_type { };

template<typename T, typename... Ts>
  struct all_same<T, T, Ts...> : all_same<T, Ts...> { };

template<typename... T>
typename std::enable_if<all_same<T...>::value, void>::type
func(T...)
{ }

Jeśli chcesz wspierać doskonałe przekazywanie, prawdopodobnie chcesz zniszczyć typy przed ich sprawdzeniem, tak aby funkcja zaakceptowała mieszankę argumentów lvalue i rvalue, o ile mają one ten sam typ:

template<typename... T>
typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
func(T&&...)
{ }

Alternatywnie, jeśli masz cechę ogólnego przeznaczenia do testowania koniunkcji logicznej, możesz to zrobić używając std::is_same zamiast pisać własne all_same:

template<typename T, typename... Ts>
typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type
func(T&&, Ts&&...)
{ }

Ponieważ wymaga to co najmniej jednego argumentu, potrzebujesz również innego przeciążenia, aby obsługiwać przypadek zerowy:

void func() { }

Helper and_ można zdefiniować następująco:

template<typename...>
  struct and_;

template<>
  struct and_<>
  : public std::true_type
  { };

template<typename B1>
  struct and_<B1>
  : public B1
  { };

template<typename B1, typename B2>
  struct and_<B1, B2>
  : public std::conditional<B1::value, B2, B1>::type
  { };

template<typename B1, typename B2, typename B3, typename... Bn>
  struct and_<B1, B2, B3, Bn...>
  : public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type
  { };
 16
Author: Jonathan Wakely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 13:47:20

Myślę, że możesz to zrobić, określając konkretny typ, gdy wygryzasz swoje argumenty z pakietu argumentów. Coś w stylu:

class MyClass{};
class MyOtherClass{};

void func()
{
    // do something
}

template< typename... Arguments >
void func( MyClass arg, Arguments ... args )
{
    // do something with arg
    func( args... );
    // do something more with arg
}


void main()
{
    MyClass a, b, c;
    MyOtherClass d;
    int i;
    float f;

    func( a, b, c );    // compiles fine
    func( i, f, d );    // cannot convert
}

W przypadku ogólnym void func( MyClass arg, Arguments ... args ) stałoby się void func( arg, Arguments ... args ) z szablonem typu T.

 10
Author: user2746401,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-11 10:36:34

@Skeen Co powiesz na to?

template <typename T>
void func_1(std::initializer_list<T>&& a) {
    // do something
} 

template <typename... T>
void func(T&&... a) {
    func_1({std::forward<T>(a)...});
} 

int main() {
    func(1, 2, 3);
    // func(1, 2, 3, 4.0); // OK doesn't compile
}
 4
Author: dusketha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-16 13:16:52

Jeśli nie chcesz używać brace-based initializer_list/vector aby zachować argumenty oddzielnie w postaci pakietu argumentów, poniżej rozwiązanie sprawdza je w czasie kompilacji używając rekurencyjnego static_assert s:

#include<type_traits>

template<typename T1, typename T2, typename... Error>
struct is_same : std::false_type {};

template<typename T, typename... Checking>
struct is_same<T, T, Checking...> : is_same<T, Checking...> {}; 

template<typename T>
struct is_same<T,T> : std::true_type {};

template<typename... LeftMost>
void func (LeftMost&&... args)
{
  static_assert(is_same<typename std::decay<LeftMost>::type...>::value, 
                "All types are not same as 'LeftMost'");
  // ...
}

int main ()
{
  int var = 2;
  func(1,var,3,4,5);  // ok
  func(1,2,3,4.0,5); // error due to `static_assert` failure
}

Właściwie to rozwiązanie sprawdzałoby wszystkie argumenty w odniesieniu do pierwszego argumentu. Przypuśćmy, że było to double wtedy wszystko byłoby sprawdzane przeciwko double.

 2
Author: iammilind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 04:01:01

Ponieważ wydaje mi się, że nie widziałem tego rozwiązania, możesz napisać konkretną funkcję dla każdego typu (w Twoim przypadku po prostu int), a następnie funkcję przekierowującą przyjmującą zmienne typy argumentów.

Napisz każdy konkretny przypadek:

Wtedy dla każdego konkretnego przypadku:

// only int in your case
void func(int i){
    std::cout << "int i = " << i << std::endl;
}

Następnie funkcja przekierowywania wygląda tak:

template<typename Arg0, typename Arg1 typename ... Args>
void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
    func(std::forward<Arg0>(arg0));
    func(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}

Jest to dobre, ponieważ można go rozszerzyć, gdy chcesz zaakceptować może inny typ też.

Używane w ten sposób:

int main(){
    func(1, 2, 3, 4); // works fine
    func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
}
 1
Author: CoffeeandCode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 06:07:07