2015-10-29 8 views
5

Ich versuche, eine Funktion zu schreiben, die eine Teilmenge eines variadic Argument-Pakets in Form einer std::tuple zurückgibt. Die Funktion sollte idealerweise keinen Laufzeit-Overhead haben (keine unnötigen Kopien), und es sollte Benutzern ermöglichen, auf lvalue Referenzen zuzugreifen und sie zu modifizieren.Entfernen Sie Rvalueness, behalten Lvalue Referenzen (Standard-Typ Merkmal verfügbar?)

Werttypen, lvalue Referenzen und const lvalue Referenzen sollten beibehalten werden. Provisionen (rvalue Verweise) sollten in Werttypen "konvertiert" werden, um zu vermeiden, dass ungültige Verweise (Verweise auf Provisorien) erstellt werden.

Beispiel für die gewünschten Ergebnisse:

int lr = 5; 
const int& clr = lr; 

auto t = make_subpack_tuple(lr, clr, 5); 

static_assert(is_same 
< 
    decltype(t), 
    std::tuple<int&, const int&, int> 
>{}, ""); 

// Ok, modifies lr: 
std::get<0>(t) = 10; 

// Compile-time error, intended: 
// std::get<1>(t) = 20; 

// Ok, 5 was moved into the tuple: 
std::get<2>(t) = 30; 

Beispiel unvollständige Umsetzung:

template<typename... Ts> 
auto make_subpack_tuple(Ts&&... xs) 
{ 
    return std::tuple 
    < 
     some_type_trait<decltype(xs)>... 
    > 
    (
     std::forward<decltype(xs)>(xs)... 
    ); 
} 

Macht das, was ich versuche, Sinn machen?

Gibt es ein Standard-Typenmerkmal, das anstelle von some_type_trait verwendet werden kann? Oder sollte ich meine eigene Lösung implementieren?

+0

nur bin ich neugierig. Warum interessiert dich das? Welches Problem lösen Sie? –

+0

Ich implementiere etwas ähnlich wie ein 'static_for', das ein Callable Object über heterogene Werte mit einer benutzerdefinierten Arity ausführt, und erlaubt dem Benutzer die aktuelle Iterationsnummer zur Kompilierzeit abzurufen und zu brechen/fortzusetzen * (früher beenden) * zur Kompilierzeit mit 'static_if'. Ein Teil der Implementierung erfordert das Übergeben der ersten "N" Argumente des variadischen Argumentpakets an eine andere innere Funktion, und ich habe versucht, dies zu verallgemeinern, indem ich einige variadische Argumentpaketmanipulationsfunktionen definiert habe. Anders als 'nth ' benötige ich 'subpack ', um dieses Verhalten vollständig zu verallgemeinern. –

+0

Durch user-spezifizierte Arity, meine ich, dass das 'static_for' über die heterogenen Werte in Gruppen von' N' iteriert (wobei 'N' ist) Vorlagenparameter vom Benutzer angegeben). Das aufrufbare Objekt, das den Körper des 'static_for' darstellt, muss einen' operator() 'mit der gleichen Arity haben. Außerdem mache ich das alles für eine Open-Source-C++ 14-Universalbibliothek ([vrm_core] (https://github.com/SuperV1234/vrm_core)), die aus Spaß und zu Lernzwecken geschrieben wurde. Hoffe das beantwortet deine Frage :) –

Antwort

7

Die Lösung für Sie

template<typename... Ts> 
auto make_subpack_tuple(Ts&&... xs) 
{ 
    return std::tuple<Ts...>(std::forward<Ts>(xs)...); 
} 

sein Nach template argument deduction rules, wird der Parameter Pack Ts... nur cv-qualifizierte Typen und lvalues ​​enthalten. Die Information in this question kann auch nützlich sein.

1

Ich würde nur gerne hereinspielen, dass ich in das gleiche nicht-wirklich-ein-Problem ("Ich glaube, ich muss Rvalue Referenzen zerfallen und lvalue Referenzen unberührt") während der Implementierung einer effizienten Version von Nick Athanasios's foldable Op<operation>. Ich hatte dieses Chaos hatte:

template<class Pack, class Op> 
struct Foldable 
{ 
    mystery_trait_t<Pack> value; 
    const Op& op; 

    template<class RhsPack> 
    auto operator*(const Foldable<RhsPack, Op>& rhs) const { 
     return op(static_cast<std::decay_t<Pack>>(
      (op.f)(std::move(value), std::move(rhs.value)) 
     )); 
    } 

    operator mystery_trait_t<Pack>() && { 
     return std::move(value); 
    } 
}; 

template<class Pack> 
auto NamedOperator::operator()(Pack&& value) const { 
    return Foldable<Pack, NamedOperator>(std::forward<Pack>(value), *this); 
} 

und (nach ein wenig rätselhaft, und dann eine Frage SO zu fragen beginnen, und daran, diese bestehende Frage/Antwort, und das Hinzufügen einer static_assert meiner Implementierung von mystery_trait_t zu überprüfen, ob es wurde nie mit einem R-Wert Referenztyp aufgerufen tatsächlich!) es stellte sich heraus, dass alles, was ich war eigentlich

template<class Pack, class Op> 
struct Foldable 
{ 
    Pack value; 
    const Op& op; 

    template<class RhsPack> 
    auto operator*(const Foldable<RhsPack, Op>& rhs) const { 
     return op(
      (op.f)(std::move(value), std::move(rhs.value)) 
     ); 
    } 

    operator Pack() && { 
     return std::move(value); 
    } 
}; 
benötigt

(See my whole code on Wandbox.)

Diese „Antwort“ von mir keine neuen Informationen beiträgt, aber ich dachte, es wäre nützlich zu teilen, weil es nur zu zeigen, dass selbst wenn Sie denken, Sie sind Weise tief in Vorlage Metaprogrammierung und sind sicher Sie brauchen diese "bedingte Verfall" Verhalten ... Sie brauchen es wirklich nicht !

Es könnte eine logische allgemeine Regel geben, dass Schreiben any_template<T&&> immer ein Code-Geruch ist. In Vittorio ursprünglichen Frage, er tat es effektiv, dass zweimal, obwohl beide Male durch decltype Syntax verborgen war:

some_type_trait<decltype(xs)>... // should have been `Ts...` 
std::forward<decltype(xs)>(xs)... // could equally well be `std::forward<Ts>(xs)...`