2016-05-03 50 views
1

的元組的深度複製糾正我,如果我錯了,但運行實現指針

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::get<0>(t) = 0x1f13d0 
std::get<1>(t) = 0x1f13b0 
std::get<0>(n) = 0x1f13d0 
std::get<1>(n) = 0x1f13b0 

這意味着在元組的指針是淺拷貝只,對?所以我寫了一個簡單的工具,旨在深副本是指向一個元組的所有元素:

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>{}); 
} 

這似乎爲例子做工精細以上,但是當我的元組tup嘗試通過

定義
std::tuple<double*, bool*> t(new double(3.5), new bool(true)); 
std::tuple<int*, std::tuple<double*, bool*>*, char> tup(new int(5), &t, 'a'); 

我得到了元組指針的深層拷貝,但是元組指針內的指針又被淺拷貝了。我希望這些指針也能被深度複製。如何解決這個問題的任何數量的嵌套指針?下面是我的測試結果:

#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)); 
} 

輸出:

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

我對此並不太熟悉,但是我懷疑如果當前的元組元素本身是一個元組,則需要遞歸調用'deep_copy_impl'中的'depp_copy'。 –

+0

啊!我會嘗試。這可能需要部分專業化的結構。 – prestokeys

+0

確實發佈了答案,如果你想出來的話!我很好奇,看看它是如何擺出來的;) 其實T.C.解決方案看起來很優雅 –

回答

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

謝謝,一如既往。是否需要前兩項前瞻性聲明?我測試了他們在我的具體示例中不需要,但他們是否需要其他情況? – prestokeys

+0

@prestokeys並非如此,但將整個超載設置出來並且不必擔心訂單是很好的。 –

0

擴大對TC的想法,STL容器以及:

#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'; 
} 

輸出:

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