2016-05-03 9 views
1

mich korrigieren, wenn ich falsch bin, aber wennImplementierung tief Kopieren von Tupel von Zeigern

std::tuple<double*, bool*> t(new double(3.5), new bool(true)); 
print_tuple(t); 
std::tuple<double*, bool*> n = t; 
print_tuple(n); 

I

std::get<0>(t) = 0x1f13d0 
std::get<1>(t) = 0x1f13b0 
std::get<0>(n) = 0x1f13d0 
std::get<1>(n) = 0x1f13b0 

zum Laufen bringen, die die Zeiger in der Tupel bedeutet nur seichte kopiert, Recht? Also schrieb ich ein einfaches Dienstprogramm, das zu tiefer Kopie gemeint ist, alle Elemente eines Tupels, die Zeiger sind:

template <std::size_t N, typename Tuple> 
std::enable_if_t<std::is_pointer<std::tuple_element_t<N, Tuple>>::value> assign (Tuple& tuple, const Tuple& other) { 
    std::get<N>(tuple) = new std::remove_pointer_t<std::tuple_element_t<N, Tuple>>(*std::get<N>(other)); 
} 

template <std::size_t N, typename Tuple> 
std::enable_if_t<!std::is_pointer<std::tuple_element_t<N, Tuple>>::value> assign (Tuple& tuple, const Tuple& other) { 
    std::get<N>(tuple) = std::get<N>(other); 
} 

template <typename Tuple, std::size_t... Is> 
Tuple deep_copy_impl (const Tuple& other, std::index_sequence<Is...>) { 
    Tuple tuple = {}; 
    const int a[] = {(assign<Is>(tuple, other), 0)...}; 
    static_cast<void>(a); 
    return tuple; 
} 

template <typename Tuple> 
Tuple deep_copy (const Tuple& other) { 
    return deep_copy_impl(other, std::make_index_sequence<std::tuple_size<Tuple>::value>{}); 
} 

Und dies scheint für das Beispiel gut zu funktionieren oben, aber wenn ich es mit Tupel versuche tup definiert durch

Ich bekomme eine tiefe Kopie des Tupelzeigers, aber die Zeiger innerhalb des Tupelzeigers werden wieder flach kopiert. Ich möchte, dass diese Zeiger auch tief kopiert werden. Wie behebt man dies für eine beliebige Anzahl von verschachtelten Tupel von Zeigern? Hier sind meine Testergebnisse:

#include <iostream> 
#include <type_traits> 
#include <utility> 
#include <tuple> 

template <std::size_t N, typename Tuple> 
std::enable_if_t<std::is_pointer<std::tuple_element_t<N, Tuple>>::value> assign (Tuple& tuple, const Tuple& other) { 
    std::get<N>(tuple) = new std::remove_pointer_t<std::tuple_element_t<N, Tuple>>(*std::get<N>(other)); 
} 

template <std::size_t N, typename Tuple> 
std::enable_if_t<!std::is_pointer<std::tuple_element_t<N, Tuple>>::value> assign (Tuple& tuple, const Tuple& other) { 
    std::get<N>(tuple) = std::get<N>(other); 
} 

template <typename Tuple, std::size_t... Is> 
Tuple deep_copy_impl (const Tuple& other, std::index_sequence<Is...>) { 
    Tuple tuple = {}; 
    const int a[] = {(assign<Is>(tuple, other), 0)...}; 
    static_cast<void>(a); 
    return tuple; 
} 

template <typename Tuple> 
Tuple deep_copy (const Tuple& other) { 
    return deep_copy_impl(other, std::make_index_sequence<std::tuple_size<Tuple>::value>{}); 
} 

// Testing 
template <typename Tuple, std::size_t... Is> 
std::ostream& print_tuple_impl (const Tuple& tuple, std::ostream& os, std::index_sequence<Is...>) { 
    const int a[] = {(os << "std::get<" << Is << ">(tuple) = " << std::get<Is>(tuple) << '\n', 0)...}; 
    static_cast<void>(a); 
    return os; 
} 

template <typename Tuple> 
std::ostream& print_tuple (const Tuple& tuple, std::ostream& os = std::cout) { 
    return print_tuple_impl (tuple, os, std::make_index_sequence<std::tuple_size<Tuple>::value>{}); 
} 

int main() { 
    std::tuple<double*, bool*> t(new double(3.5), new bool(true)); 
    print_tuple(t); 
    std::tuple<double*, bool*> n = t; 
    print_tuple(n); 
    std::cout << "Above is shallow copying only.\n\n"; 

    std::tuple<int*, std::tuple<double*, bool*>*, char> tup(new int(5), &t, 'a'); 
    print_tuple(tup); 
    std::tuple<int*, std::tuple<double*, bool*>*, char> q = deep_copy(tup); 
    print_tuple(q); 
    std::cout << "\nAbove seems like a deep copy, but look at this:\n"; 

    print_tuple(*std::get<1>(tup)); 
    print_tuple(*std::get<1>(q)); 
} 

Ausgang:

std::get<0>(tuple) = 0x1f13d0 
std::get<1>(tuple) = 0x1f13b0 
std::get<0>(tuple) = 0x1f13d0 
std::get<1>(tuple) = 0x1f13b0 
Above is shallow copying only. 

std::get<0>(tuple) = 0x1f13f0 
std::get<1>(tuple) = 0x72fe10 
std::get<2>(tuple) = a 
std::get<0>(tuple) = 0x1f1410 
std::get<1>(tuple) = 0x1f1430 
std::get<2>(tuple) = a 

Above seems like a deep copy, but look at this: 
std::get<0>(tuple) = 0x1f13d0 
std::get<1>(tuple) = 0x1f13b0 
std::get<0>(tuple) = 0x1f13d0 
std::get<1>(tuple) = 0x1f13b0 
+0

Ich bin nicht so vertraut, aber ich vermute, dass Sie 'depp_copy' in' deep_copy_impl' rekursiv aufrufen müssen, wenn das aktuelle Tupel-Element selbst ein Tupel ist. –

+0

Ah! Ich werde es versuchen. Dies könnte dann eine teilweise Spezialisierung von Strukturen erfordern. – prestokeys

+0

posten Sie die Antwort, wenn Sie es herausfinden! Ich bin gespannt, wie es ausgeht;) Eigentlich T.C. Lösung scheint ziemlich elegant. –

Antwort

4
template<class T> T deep_copy(const T& t); 
template<class T> T* deep_copy(T* tp); 
template<class... Ts> std::tuple<Ts...> deep_copy(const std::tuple<Ts...>&); 

template<class T> 
T deep_copy(const T& t) { return t; } 

template<class T> 
T* deep_copy(T* tp) { return new T(deep_copy(*tp)); } 

template<class... Ts, size_t... Is> 
std::tuple<Ts...> deep_copy_impl(const std::tuple<Ts...>& t, std::index_sequence<Is...>) { 
    return std::tuple<Ts...>{deep_copy(std::get<Is>(t))... }; 
} 

template<class... Ts> 
std::tuple<Ts...> deep_copy(const std::tuple<Ts...>& t) { 
    return deep_copy_impl(tuple, std::index_sequence_for<Ts...>()); 
} 
+0

Danke, wie immer. Sind die ersten 2 Vorwärtsdeklarationen erforderlich? Ich habe getestet, dass sie in meinem speziellen Beispiel nicht benötigt werden, aber werden sie für andere Fälle benötigt? – prestokeys

+0

@ prestokeys Nicht wirklich, aber es ist schön, den ganzen Überladungssatz angelegt zu haben und sich nicht um die Reihenfolge kümmern zu müssen. –

0

Aufbauend auf TC Idee zu STL-Containern auch:

#include <iostream> 
#include <type_traits> 
#include <utility> 
#include <tuple> 
#include <vector> 
#include <set> 

template <typename T> 
using void_t = void; 

template <typename T, typename = void> 
struct has_emplace_back : std::false_type {}; 

template <typename T> 
struct has_emplace_back<T, void_t<decltype(std::declval<T>().emplace_back(std::declval<typename T::value_type>()))>> : std::true_type {}; 

template <typename T, typename = void> 
struct has_emplace : std::false_type {}; 

template <typename T> 
struct has_emplace<T, void_t<decltype(std::declval<T>().emplace(std::declval<typename T::value_type>()))>> : std::true_type {}; 
// etc... for other container types. 

template <typename T> 
struct is_stl_container : std::integral_constant<bool, has_emplace_back<T>::value || has_emplace<T>::value> {}; 

template <typename Container> std::enable_if_t<has_emplace_back<Container>::value, Container> deep_copy (const Container&); 
template <typename Container> std::enable_if_t<has_emplace<Container>::value, Container> deep_copy (const Container&); 
template <typename... Ts> std::tuple<Ts...> deep_copy (const std::tuple<Ts...>&); // This forward declarations, though not needed to compile, is needed for the nested deep copying to work correctly. 

template <typename T> 
std::enable_if_t<!is_stl_container<T>::value, T> deep_copy (const T& t) { 
    return t; 
} 

template <typename T> 
T* deep_copy (T* t) { 
    return new T(deep_copy(*t)); // Note that since T's copy constructor is called here, then if T is a custom class that has its own custom copy constructor that carries out deep copying, then t's pointer data members are also deep copied. 
} 

template <typename... Ts, std::size_t... Is> 
std::tuple<Ts...> deep_copy_impl (const std::tuple<Ts...>& tuple, std::index_sequence<Is...>) { 
    return std::tuple<Ts...>{ deep_copy(std::get<Is>(tuple))... }; 
} 

template <typename... Ts> 
std::tuple<Ts...> deep_copy (const std::tuple<Ts...>& tuple) { 
    return deep_copy_impl(tuple, std::index_sequence_for<Ts...>{}); 
} 

template <typename Container> 
std::enable_if_t<has_emplace_back<Container>::value, Container> deep_copy (const Container& c) { 
    Container container; 
    for (const typename Container::value_type& t : c) 
     container.emplace_back(deep_copy(t)); 
    return container; 
} 

template <typename Container> 
std::enable_if_t<has_emplace<Container>::value, Container> deep_copy (const Container& c) { 
    Container container; 
    for (const typename Container::value_type& t : c) 
     container.emplace(deep_copy(t)); 
    return container; 
} 

// Testing 
template <typename Tuple, std::size_t... Is> 
std::ostream& print_tuple_impl (const Tuple& tuple, std::ostream& os, std::index_sequence<Is...>) { 
    const int a[] = {(os << "std::get<" << Is << ">(tuple) = " << std::get<Is>(tuple) << '\n', 0)...}; 
    static_cast<void>(a); 
    return os; 
} 

template <typename Tuple> 
std::ostream& print_tuple (const Tuple& tuple, std::ostream& os = std::cout) { 
    return print_tuple_impl (tuple, os, std::make_index_sequence<std::tuple_size<Tuple>::value>{}); 
} 

int main() { 
    std::tuple<double*, bool*> t(new double(3.5), new bool(true)); 
    print_tuple(t); 
    std::tuple<double*, bool*> n = t; 
    print_tuple(n); 
    std::cout << "Above is shallow copying only.\n\n"; 

    std::tuple<int*, std::tuple<double*, bool*>*, char> tup(new int(5), &t, 'a'); 
    print_tuple(tup); 
    std::tuple<int*, std::tuple<double*, bool*>*, char> q = deep_copy(tup); 
    print_tuple(q); 
    std::cout << "\nAbove and below show that we have full deep copying:\n"; 

    print_tuple(*std::get<1>(tup)); 
    print_tuple(*std::get<1>(q)); 

    std::cout << "\nDeep copy of a vector of pointers:\n"; 
    std::vector<int*> v = {new int(5), new int(2), new int(8)}; 
    for (int* x : v) std::cout << x << ' '; std::cout << '\n'; 
    std::vector<int*> u = deep_copy(v); 
    for (int* x : u) std::cout << x << ' '; std::cout << '\n'; 

    std::cout << "\nDeep copy of a set of pointers:\n"; 
    std::set<int*> s = {new int(5), new int(2), new int(8)}; 
    for (int* x : s) std::cout << x << ' '; std::cout << '\n'; 
    std::set<int*> ss = deep_copy(s); 
    for (int* x : ss) std::cout << x << ' '; std::cout << '\n'; 
} 

Ausgang:

std::get<0>(tuple) = 0x8513d0 
std::get<1>(tuple) = 0x8513b0 
std::get<0>(tuple) = 0x8513d0 
std::get<1>(tuple) = 0x8513b0 
Above is shallow copying only. 

std::get<0>(tuple) = 0x8513f0 
std::get<1>(tuple) = 0x72fd70 
std::get<2>(tuple) = a 
std::get<0>(tuple) = 0x851410 
std::get<1>(tuple) = 0x851470 
std::get<2>(tuple) = a 

Above and below show that we have full deep copying: 
std::get<0>(tuple) = 0x8513d0 
std::get<1>(tuple) = 0x8513b0 
std::get<0>(tuple) = 0x851430 
std::get<1>(tuple) = 0x851450 

Deep copy of a vector of pointers: 
0x851490 0x8514b0 0x8514d0 
0x851510 0x851550 0x851530 

Deep copy of a set of pointers: 
0x851570 0x8515c0 0x8515e0 
0x851690 0x851800 0x851870