Określanie jednego typu dla wszystkich argumentów przekazywanych do funkcji zmiennej lub zmiennej funkcji szablonu z / out za pomocą tablicy, wektora, struktur itp.?

Tworzę funkcję (ewentualnie funkcję member, Nie żeby to miało znaczenie... może tak jest?), który musi zaakceptować nieznaną liczbę argumentów, ale chcę, aby wszystkie były tego samego typu. Wiem, że mógłbym przekazać tablicę lub wektor, ale chcę być w stanie zaakceptować listę args bezpośrednio bez dodatkowej struktury lub nawet dodatkowych nawiasów. Nie wygląda na to, że różne funkcje same w sobie są typami, a ja nie byłem pewien, jak się do tego zabrać w / variadic template functions. Oto zasadniczo to, do czego zmierzam (bardziej niż prawdopodobne, że nie poprawny kod, a zupełnie nie w celu uzyskania list smoków, lol): {]}

//typedef for dragon_list_t up here somewhere.

enum Maiden {
    Eunice
    , Beatrice
    , Una_Brow
    , Helga
    , Aida
};

dragon_list_t make_dragon_list(Maiden...) {
    //here be dragons
}

Lub

template<Maiden... Maidens> dragon_list_t make_dragon_list(Maidens...) {
    //here be dragons
}

Użycie

dragon_list_t dragons_to_slay
    = make_dragon_list(Maiden.Eunice, Maiden.Helga, Maiden.Aida)
;

Próbowałem już kilku rzeczy podobnych do powyższych, bez kości. Sugestie? Oczywiste przeoczenia, które mogłem zrobić? Wiem, że to może nie być wielka sprawa zrobić to zamiast:

dragon_list_t make_dragon_list(std::array<Maiden> maidens) {
    //here be dragons.
}
dragon_list_t dragons_to_slay
    = make_dragon_list({Maiden.Eunice, Maiden.Helga, Maiden.Aida})
;
[4]}ale wolałbym być w stanie zrobić to pierwszy sposób, jeśli to możliwe.
Author: Johannes Schaub - litb, 2010-09-13

9 answers

Możesz po prostu zaakceptować argumenty za pomocą szablonu zmiennego i pozwolić typechecking sprawdzić ważność później, gdy zostaną przekonwertowane.

Możesz sprawdzić konwertowalność na poziomie interfejsu funkcji, aby użyć rozdzielczości przeciążenia do odrzucania błędnych argumentów, na przykład za pomocą SFINAE

template<typename R, typename...> struct fst { typedef R type; };

template<typename ...Args>
typename fst<void, 
  typename enable_if<
    is_convertible<Args, ToType>::value
  >::type...
>::type 
f(Args...);

Dla Twojego przypadku użycia jeśli znasz kroki, aby przejść od std::array<> do twojego dragon_list_t, to już go rozwiązałeś, chociaż zgodnie z pierwszą opcją powyżej ("convert-later"):

template<typename ...Items>
dragon_list_t make_dragon_list(Items... maidens) {
    std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }};
    // here be dragons
}

Jeśli połączysz to z powyższym podejściem is_convertible, masz szablon Odrzuć-wcześnie, który również przeciąża rozdzielczość argumentów i odrzuca je, jeśli nie ma zastosowania.

 26
Author: Johannes Schaub - litb,
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
2010-09-13 20:18:27

Jeśli nie użyjesz template na parametrze, który nie znajduje się w pakiecie, funkcja zmienna będzie miała wszystkie argumenty tego samego typu.

Oto przykład rozszerzonej funkcji max, która akceptuje tylko ints (lub typy zamienione na int).

int maximum(int n) // last argument must be an `int`
{
    return n;
}

template<typename... Args>
int maximum(int n, Args... args) // first argument must be an int
{
    return std::max(n, maximum(args...));
}

Explanation: kiedy rozpakujesz argument pack (args...) kompilator szuka najlepszego przeciążenia. Jeśli paczka miała tylko jeden parametr to jedynym kandydatem jest maximum(int) więc jedynym parametrem musi być i typu int (lub zamiennie do int). Jeśli w pakiecie jest więcej niż jeden element, to jedynym kandydatem jest maximum(int, typename...), więc pierwszy argument musi być typu int (lub zamienny do int). Łatwo jest udowodnić indukcyjnie, że wszystkie typy w paczce muszą być typu int).

 10
Author: Motti,
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
2010-09-14 08:15:07

Ponieważ dodałeś znacznik C++0x, oczywistą odpowiedzią byłoby wyszukanie list inicjujących . Lista inicjalizatorów pozwala określić liczbę argumentów dla ctor, które zostaną automatycznie przekonwertowane do pojedynczej struktury danych do przetworzenia przez ctor.

Ich główny (Wyłączny?) use służy do dokładnie takiej sytuacji, o której wspomniałeś, przekazując szereg argumentów tego samego typu do użycia w tworzeniu jakiejś listy/tablicy / innej kolekcji obiektów. Będzie wspierane przez (na przykład) std::vector, więc możesz użyć czegoś takiego jak:

std::vector<dragon> dragons_to_slay{Eunice, Helga, Aida};

Aby utworzyć wektor trzech dragon obiektów. Większość (wszystkie?) z innych kolekcji będzie to samo, więc jeśli naprawdę nalegać na listę smoków powinno być w stanie uzyskać to dość łatwo, jak również.

 10
Author: Jerry Coffin,
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
2016-07-22 04:14:39

Ostatnio musiałem ograniczyć parametr pack, aby był tylko jeden typ, lub przynajmniej zamienny dla tego typu. Skończyło się na znalezieniu innego sposobu:

#include <type_traits>
#include <string>

template <template<typename> class Trait, typename Head, typename ...Tail> 
struct check_all {
  enum { value = Trait<Head>::value && check_all<Trait, Tail...>::value };
};

template <template<typename> class Trait, typename Head>
struct check_all<Trait, Head> {
  enum { value = Trait<Head>::value };
};

template <typename ...Args> 
struct foo {
  // Using C++11 template alias as compile time std::bind
  template <typename T>
  using Requirement = std::is_convertible<double, T>;
  static_assert(check_all<Requirement, Args...>::value, "Must convert to double");
};

int main() {
  foo<int, char, float, double>();
  foo<int, std::string>(); // Errors, no conversion
}

To, co mi się podobało w tym rozwiązaniu, to to, że mogę zastosować check_all również do innych cech.

 4
Author: Flexo,
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
2012-07-10 13:42:04

To naprawdę zależy, co próbujesz zaimplementować, dokładnie.

Zazwyczaj enum wskazuje podtypy runtime danej klasy lub dyskryminowaną Unię (boost::variant). Ale w tym przypadku, chcesz przekazać enum bezpośrednio. Co więcej, masz ograniczony zbiór możliwych wartości, a każde wywołanie funkcji tworzy podzbiór. Tak naprawdę to, co reprezentujesz, to jeden podzbiór, a nie kilka parametrów.

Najlepszym sposobem reprezentowania podzbioru skończonego zbioru jest bitset. Duże zestawy powinno używać std::bitset; małe zestawy mogą po prostu używać unsigned long.

enum Maiden_set {
    Eunice = 1,
    , Beatrice = 2
    , Una_Brow = 4
    , Helga = 8
    , Aida = 16
};

dragon_list_t make_dragon_list(Maiden_set) {
    //here be dragons
}

make_dragon_list( Eunice + Beatrice + Helga );

Lub, ponieważ wydaje się, że chcesz obsłużyć zmiany w czasie kompilacji,

template< int Maidens > // parameter is not a Maiden_set because enum+enum=int
dragon_list_t make_dragon_list() {
    //here be dragons
}

make_dragon_list< Eunice + Beatrice + Helga >(); // + promotes each enum to int

Powinno być możliwe wygenerowanie mocy 2 automatycznie za pomocą operator+ przeciążonego na typ enum. Ale nie jestem pewien, czy jestem na dobrej drodze.

 1
Author: Potatoswatter,
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
2010-09-13 19:33:30

Starałbym się, aby wszystko było proste, a najprostszym rozwiązaniem, jakie przychodzi mi do głowy, jest użycie zwykłego starego wektora. Używając funkcji C++0x możesz uzyskać składnię podobną (nawet jeśli nie do końca) do tego, czego chcesz:

void foo( std::vector<int> const & v ) {
   std::copy( v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ") );
}
int main() {
  foo({ 1, 2, 3, 4, 5, 6 }); // note the extra {}
}
 1
Author: David Rodríguez - dribeas,
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
2010-09-13 20:13:04

Krótko mówiąc, prawdopodobnie powinieneś po prostu utworzyć wektor. Nie jest to zbyt duże obciążenie, zwłaszcza jeśli używasz czegoś takiego jak boost:: list_of lub lista inicjalizatorów C++0x. Narzut składniowy jest minimalny i bardziej elastyczny (można przekazać listę z wieloma argumentami znanymi tylko w trybie runtime).

Jeśli naprawdę chcesz, możesz użyć do tego zmiennych parametrów szablonu:

// Using pass-by-value since I'm assuming it is primitive:

template< typename T, typename... Args>
void make_dragon_list_internal( dragon_list_t* dragon_list, T t, Args... args )
{
   // add T to dragon_list.
   make_dragon_list_internal( dragon_list, args... );
}

void make_dragon_list_internal( dragon_list_t* dragon_list )
{
   // Finalize dragon_list.
}

template<typename... Args>
dragon_list_t make_dragon_list( Args... args )
{
  dragon_list_t dragon_list;
  make_dragon_list_internal( &dragon_list, args... );
  return dragon_list;
}

Jest typesafe, i rozszerzalny (można to zrobić, aby wziąć rzeczy inne niż smoki, jeśli masz ochotę).

 1
Author: Todd Gardner,
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
2010-09-13 20:18:28

Chociaż pytanie jest oznaczone C++11, myślę, że rozwiązanie C++17 + concepts byłoby warte dodania, ponieważ teraz jest wsparcie w GCC, a inne wkrótce nastąpią.

Najpierw zdefiniuj proste pojęcie

class mytype{};

template<typename T>
concept bool MyType = std::is_same<T, mytype>::value;

Następnie po prostu użyj zmiennych parametrów szablonu

template<MyType ... Args>
void func(Args &&... args){
    // do something here
}
O wiele łatwiej z pojawieniem się pojęć!
 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
2016-06-16 01:56:36

Myślę, że poniższy kod jest pomocny w twojej sprawie:

template <class...>
struct IsAllSame {};

template <class T, class B1>
struct IsAllSame<T, B1> {
  static constexpr const bool kValue = std::is_same<T, B1>::value;
};

template <class T, class B1, class... Bn>
struct IsAllSame<T, B1, Bn...> {
  static constexpr const bool kValue =
      IsAllSame<T, B1>::kValue ? IsAllSame<T, Bn...>::kValue : false;
};

IsAllSame<int>::kValue == true
IsAllSame<bool, int>::kValue == false
IsAllSame<bool, int, int>::kValue == false
 0
Author: Nan Hua,
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-11-03 23:22:53