2016-08-03 11 views
3

Angenommen, ich habe ein ObjektGibt es eine Möglichkeit ein Objekt Feld implizit in C auf eine Funktion zu übergeben ++

class Obj { 
public: 
    int a; 
    int b; 
    int c; 
} 

Und ein Array von Objekten

Obj o[N]; 

Ich mag jede Obj.a kopieren einen int-Array in und ich weiß, andere Sprachen erlauben Sie mir, eine Funktion zu machen, wie dies in C++ aussehen könnte

int & fun(Obj os[], T key, int N){ 
    int a[N]; 
    for (int i=0; i<N; i++) { 
     a[i] = os[i].key; 
    } 
    return a; 
} 

gibt es ein wiederverwendbare Möglichkeit, dies in C++ zu tun? Als Referenz kann Objs Code nicht geändert werden.

+2

Ja es gibt! Verwenden Sie kein C-artiges Array und alles wird gut! – mash

+1

"a" als Referenz zurückgeben ist UB. Müssen Sie diese Mitglieder bearbeiten können? – LogicStuff

+0

@mash Sie missverstanden wahrscheinlich die Frage (es sei denn, es ist Ironie?) – fdreger

Antwort

7

Dies ist, was die std::transform Funktion ist. Alles, was Sie zur Verfügung stellen müssen, ist eine Funktion, um das gewünschte Element aus einer Obj zu erhalten. Dieses Beispiel zeigt, wie es mit std::mem_fn zu tun:

#include <algorithm> 
#include <functional> 
#include <iterator>  
#include <iostream> 

struct Obj { int a, b, c; }; 

int main() { 

    Obj o[3] = {{1, 2, 3}, {11, 22, 33},{111, 222, 333}}; 

    int a[3]; 

    std::transform(std::begin(o), std::end(o), 
        std::begin(a), 
        std::mem_fn(&Obj::a)); 

    for (auto e : a) 
     std::cout << e << ' '; 

    std::cout << std::endl; 
}; 

Ausgang:

in einer Hilfsfunktion werden diese alle eingewickelt der Anrufer zu ermöglichen, einzustellen das zu extrahierende Attribut Wenn Sie jedoch wirklich möchten, dass eine Funktion ein Array zurückgibt, müssen Sie einen kopierbaren Typ wie oder std::vector verwenden. In C++ sind einfache Arrays nicht kopierbar und können daher nicht als Wert von einer Funktion zurückgegeben werden.

+1

Eine Alternative zu '[] (const Obj & e) {return e.a;}' könnte 'std :: mem_fn (& Obj :: a);' sein, was weniger Tastenanschläge erfordert. – Nawaz

+0

@Nawaz Ja, danke. Ich arbeite daran! – juanchopanza

4

Hier ist eine leicht modifizierte Version des Codes:

#include <cstddef> 
#include <iostream> 

using std::size_t; 

struct Obj { 
    int a; 
    int b; 
    int c; 
}; 

static void fun(const Obj *os, size_t N, int Obj::*const key) { 
    for (size_t i = 0; i < N; i++) { 
     std::cout << os[i].*key << '\n'; 
    } 
} 

int main() { 
    Obj o[] = { {1, 2, 3}, {4, 5, 6} }; 
    fun(o, sizeof o/sizeof o[0], &Obj::b); 
} 

ich den Rückgabetyp von fun geändert, weil Ihre Version typecheck nicht. Zu Demonstrationszwecken gibt fun einfach die Elemente aus.

Der Schlüssel hier ist, dass Sie über Klassenfelder abstrahieren können, indem Sie member pointers verwenden.

+0

Vielen Dank! –

+0

Sie könnten die Fun-Funktion anpassen, damit sie mit verschiedenen Typen funktioniert. –

1

Eine mögliche Transkription von Ihrem Beispiel in C++ könnte sein:

#include <functional> 
int* fun(Obj os[], std::function<int(Obj)> get_key, int N){ 
    int* a = new int[N]; 
    for (int i=0; i<N; i++) { 
     a[i] = get_key(os[i]); 
    } 
    return a; 
} 

Verwendung:

func(os, [](Obj obj){return obj.a;}, N); 

Sie müssen ein dynamisches Array deklarieren, da die Größe variabel ist. Aber mit einem rohen Zeiger können Sie Speicherverlust oder Segmentierungsfehler erhalten.Eine Version mit einem verwalteten Zeiger (Arbeits nur in C++ 14):

#include <memory> 
#include <functional> 

std::unique_ptr<int[]> fun(Obj os[], std::function<int(Obj)> get_key, int N){ 
    std::unique_ptr<int[]> a = std::make_unique(N); 

    for (int i=0; i<N; i++) { 
     a[i] = get_key(os[i]); 
    } 
    return a; 
} 

Oder mit stl Containern (keine Speicherzuweisung):

#include <vector> 
#include <functional> 

std::vector<int> fun(Obj os[], std::function<int(Obj)> get_key, int N){ 
    std::vector<int> a(N); 
    for (int i=0; i<N; i++) { 
     a[i] = get_key(os[i]); 
    } 
    return a; 
} 

Aber es ist oft eine schlechte Idee zu "übersetzen" aus einer Sprache zur anderen;)

4

Sie pointer-to-Mitglied Syntax verwenden:

#include <iostream> 
#include <memory> 

struct Obj { 
    int a; 
    int b; 
}; 

std::unique_ptr<int[]> 
fn(Obj* os, size_t N, int Obj::*member) 
{ 
    auto arr = std::make_unique<int[]>(N); 
    for (size_t i = 0; i < N; ++i) { 
     arr[i] = os[i].*member; 
    } 
    return arr; 
} 

int main() { 
    Obj os[] { { 1, 10 }, { 2, 20 } }; 

    auto a1 = fn(os, 2, &Obj::a); 
    auto a2 = fn(os, 2, &Obj::b); 

    for (size_t i = 0; i < 2; ++i) { 
     std::cout << i << ": " << a1[i] << ", " << a2[i] << '\n'; 
    } 
} 

Demo: http://ideone.com/cQMyh3

Oder Sie könnten ein Lambda verwenden.

#include <iostream> 
#include <memory> 

struct Obj { 
    int a; 
    int b; 
}; 

std::unique_ptr<int[]> 
fn(Obj* os, size_t N, std::function<int(const Obj&)> keyFn) 
{ 
    auto arr = std::make_unique<int[]>(N); 
    for (size_t i = 0; i < N; ++i) { 
     arr[i] = keyFn(os[i]); 
    } 
    return arr; 
} 

int main() { 
    Obj os[] { { 1, 10 }, { 2, 20 } }; 

    auto a1 = fn(os, 2, [](const Obj& o){ return o.a; }); 
    auto a2 = fn(os, 2, [](const Obj& o){ return o.b; }); 

    for (size_t i = 0; i < 2; ++i) { 
     std::cout << i << ": " << a1[i] << ", " << a2[i] << '\n'; 
    } 
} 

http://ideone.com/9OvTzl

Oder die allgemeinere:

template<typename KeyFn> 
std::unique_ptr<int[]> 
fn(Obj* os, size_t N, KeyFn keyFn) 
{ 
    auto arr = std::make_unique<int[]>(N); 
    for (size_t i = 0; i < N; ++i) { 
     arr[i] = keyFn(os[i]); 
    } 
    return arr; 
} 

Weitere Änderungen zu berücksichtigen:

  • Verwenden Sie einen Standard-Container, z.B. vector oder array Ihre Obj der Hause,
  • Betrachten Iteratoren über die Bereiche, anstatt einen Zeiger und eine Größe,
  • Sie
+0

'std :: function' über Vorlage, warum? – mash

+1

@mash * shrug * schien es nicht wert, eine Vorlage für dieses Beispiel einzuführen. – kfsone

+1

@mash siehe bearbeiten. – kfsone

1

Mit range Bibliothek vorbei einfach

tun können
for (int e : objs | ranges::v3::view::transform(&Obj::a)) { 
    std::cout << e << " "; 
} 

Demo