2012-03-23 19 views
10

यदि std::tuple के सभी सदस्य standard layout types हैं, तो क्या std::tuple स्वयं मानक लेआउट है? उपयोगकर्ता द्वारा परिभाषित प्रति-कन्स्ट्रक्टर की उपस्थिति इसे गैर-तुच्छ बनाता है, लेकिन मैं सोच रहा था कि यह अभी भी मानक लेआउट हो सकता है या नहीं।std :: tuple और मानक लेआउट

spec से एक उद्धरण अच्छा होगा।

+0

यदि आप इसे जानना चाहते हैं क्योंकि आप अनुकूलन अवसर चाहते हैं, तो आपको 'std :: is_standard_layout' का उपयोग करना चाहिए और संकलन-समय शाखा लेनी चाहिए। फिर आप यह जानकर आराम कर सकते हैं कि आप स्वयं के प्रकार के सभी विवरणों को जानने के बिना इष्टतम हैं। – GManNickG

+0

ऐसा लगता है कि ऐसा होगा, लेकिन मुझे मानक के अनुभाग में 'मानक लेआउट' का कोई उल्लेख नहीं मिल रहा है जिसमें 'tuple' शामिल है। कहीं और उल्लेख हो सकता है, लेकिन यदि ऐसा है तो मुझे अभी तक यह नहीं मिला है। –

उत्तर

8

नहीं, मानक लेआउट की आवश्यकता है कि सभी nonstatic डेटा सदस्य या तो एक बेस सबोबजेक्ट या सीधे सबसे व्युत्पन्न प्रकार के हैं, और std::tuple के सामान्य कार्यान्वयन प्रति सदस्य वर्ग के एक सदस्य को लागू करते हैं।

क्योंकि सदस्य-घोषणा उपरोक्त आवश्यकता के प्रकाश में एक पैक विस्तार नहीं हो सकती है, मानक लेआउट tuple में एक से अधिक सदस्य नहीं हो सकते हैं। एक कार्यान्वयन अभी भी tuple "सदस्यों" कोके अंदर संग्रहीत करके और reinterpret_cast द्वारा ऑब्जेक्ट संदर्भ प्राप्त करके समस्या को दूर कर सकता है। मेटाप्रोग्राम को कक्षा लेआउट उत्पन्न करना होगा। विशेष सदस्य कार्यों को फिर से लागू करना होगा। यह काफी दर्द होगा।

+1

विशिष्ट कार्यान्वयन, हां, लेकिन _good_ कार्यान्वयन नहीं ([हावर्ड के अनुसार] (http://stackoverflow.com/a/9643480/636019): -पी); मुझे संदेह है कि वे _could_ की आवश्यकता है कि मानक-लेआउट प्रकारों के tuples केवल मानक लेआउट हो ... – ildjarn

+0

@ildjarn संपादित उत्तर देखें – Potatoswatter

+0

संपादित टिप्पणी देखें। ; -] – ildjarn

3

आलूस्वाटर के जवाब से प्रेरित, मैंने अपना दिन सी ++ 14 के लिए मानक लेआउट टुपल बनाने के लिए समर्पित किया है।

कोड वास्तव में काम करता है, लेकिन वर्तमान में उपयोग के लिए उपयुक्त नहीं है क्योंकि इसमें अपरिभाषित व्यवहार शामिल है। इसे सबूत-अवधारणा के रूप में समझें। यहाँ कोड मैं के साथ समाप्त हो गया है:

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

//get_size 
template <typename T_head> 
constexpr size_t get_size() 
{ 
    return sizeof(T_head); 
} 

template <typename T_head, typename T_second, typename... T_tail> 
constexpr size_t get_size() 
{ 
    return get_size<T_head>() + get_size<T_second, T_tail...>(); 
} 


//concat 
template<size_t N1, size_t... I1, size_t N2, size_t... I2> 
constexpr std::array<size_t, N1+N2> concat(const std::array<size_t, N1>& a1, const std::array<size_t, N2>& a2, std::index_sequence<I1...>, std::index_sequence<I2...>) 
{ 
    return { a1[I1]..., a2[I2]... }; 
} 

template<size_t N1, size_t N2> 
constexpr std::array<size_t, N1+N2> concat(const std::array<size_t, N1>& a1, const std::array<size_t, N2>& a2) 
{ 
    return concat(a1, a2, std::make_index_sequence<N1>{}, std::make_index_sequence<N2>{}); 
} 


//make_index_array 
template<size_t T_offset, typename T_head> 
constexpr std::array<size_t, 1> make_index_array() 
{ 
    return {T_offset}; 
} 

template<size_t T_offset, typename T_head, typename T_Second, typename... T_tail> 
constexpr std::array<size_t, (sizeof...(T_tail) + 2)> make_index_array() 
{ 
    return concat(
     make_index_array<T_offset, T_head>(), 
     make_index_array<T_offset + sizeof(T_head),T_Second, T_tail...>() 
    ); 
} 

template<typename... T_args> 
constexpr std::array<size_t, (sizeof...(T_args))> make_index_array() 
{ 
    return make_index_array<0, T_args...>(); 
} 


template<int N, typename... Ts> 
using T_param = typename std::tuple_element<N, std::tuple<Ts...>>::type; 


template <typename... T_args> 
struct standard_layout_tuple 
{ 
    static constexpr std::array<size_t, sizeof...(T_args)> index_array = make_index_array<T_args...>(); 

    char storage[get_size<T_args...>()]; 

    //Initialization 
    template<size_t T_index, typename T_val> 
    void initialize(T_val&& val) 
    { 
     void* place = &this->storage[index_array[T_index]]; 
     new(place) T_val(std::forward<T_val>(val)); 
    } 

    template<size_t T_index, typename T_val, typename T_val2, typename... T_vals_rest> 
    void initialize(T_val&& val, T_val2&& val2, T_vals_rest&&... vals_rest) 
    { 
     initialize<T_index, T_val>(std::forward<T_val>(val)); 
     initialize<T_index+1, T_val2, T_vals_rest...>(std::forward<T_val2>(val2), std::forward<T_vals_rest>(vals_rest)...); 
    } 

    void initialize(T_args&&... args) 
    { 
     initialize<0, T_args...>(std::forward<T_args>(args)...); 
    } 

    standard_layout_tuple(T_args&&... args) 
    { 
     initialize(std::forward<T_args>(args)...); 
    } 

    //Destruction 
    template<size_t T_index, typename T_val> 
    void destroy() 
    { 
     T_val* place = reinterpret_cast<T_val*>(&this->storage[index_array[T_index]]); 
     place->~T_val(); 
    } 

    template<size_t T_index, typename T_val, typename T_val2, typename... T_vals_rest> 
    void destroy() 
    { 
     destroy<T_index, T_val>(); 
     destroy<T_index+1, T_val2, T_vals_rest...>(); 
    } 

    void destroy() 
    { 
     destroy<0, T_args...>(); 
    } 

    ~standard_layout_tuple() 
    { 
     destroy(); 
    } 

    template<size_t T_index> 
    void set(T_param<T_index, T_args...>&& data) 
    { 
     T_param<T_index, T_args...>* ptr = reinterpret_cast<T_param<T_index, T_args...>*>(&this->storage[index_array[T_index]]); 
     *ptr = std::forward<T_param<T_index, T_args...>>(data); 
    } 

    template<size_t T_index> 
    T_param<T_index, T_args...>& get() 
    { 
     return *reinterpret_cast<T_param<T_index, T_args...>*>(&this->storage[index_array[T_index]]); 
    } 
}; 


int main() { 
    standard_layout_tuple<float, double, int, double> sltuple{5.5f, 3.4, 7, 1.22}; 
    sltuple.set<2>(47); 

    std::cout << sltuple.get<0>() << std::endl; 
    std::cout << sltuple.get<1>() << std::endl; 
    std::cout << sltuple.get<2>() << std::endl; 
    std::cout << sltuple.get<3>() << std::endl; 

    std::cout << "is standard layout:" << std::endl; 
    std::cout << std::boolalpha << std::is_standard_layout<standard_layout_tuple<float, double, int, double>>::value << std::endl; 

    return 0; 
} 

लाइव उदाहरण:

  • संरेखण ठीक से नहीं संभाला है, जिसका अर्थ है: https://ideone.com/4LEnSS

    कुछ चीजें मैं के साथ खुश नहीं हूँ गलत हस्ताक्षर किए गए प्रकारों में प्रवेश करने से आपको वर्तमान में अपरिभाषित व्यवहार मिलेगा

  • सभी टुपल कार्यक्षमता को पुनर्स्थापित नहीं किया गया है अभी तक टेड
  • मुझे नहीं लगता कि स्मृति प्रबंधन वर्तमान में अपवाद-सुरक्षित है।
  • यह प्रत्येक इंडेक्स के प्रकार को निर्धारित करने के लिए टुपल का उपयोग करता है।
  • कुल कोड गुणवत्ता थोड़ी गन्दा है।
  • कुछ पुनरावर्ती टेम्पलेट कार्यों को संभालने के लिए बेहतर, अधिक संक्षिप्त तरीके हो सकते हैं।
  • मैं जो कुछ भी किया वह पूरी तरह से समझ में नहीं आता है। मैं मुख्य मूल बातें समझता हूं, लेकिन मैं अच्छे विश्वास पर कुछ वीरडर वाक्यविन्यास का उपयोग कर रहा हूं। यहाँ सबसे महत्वपूर्ण स्रोत मैं प्रयोग किया जाता है:

यह नहीं के रूप में है उपयोग के लिए उपयुक्त अभी तक है, वास्तव में केवल इसका इलाज करें इस राज्य में एक सबूत-अवधारणा के रूप में। मैं शायद इन मुद्दों में से कुछ पर सुधार करने के लिए वापस आऊंगा।या, यदि कोई और इसे सुधार सकता है, तो संपादित करने के लिए स्वतंत्र महसूस करें।

0

एक कारण std::tuple मानक लेआउट का नहीं हो सकता है, क्योंकि सदस्यों के साथ किसी भी वर्ग और सदस्यों के साथ बेस क्लास, यह मानक है कि मानक खाली कक्षाओं को प्राप्त करते समय मानक अनुकूलन के लिए अनुमति देता है।

#include <cstdio> 
#include <cstdint> 

class X 
{ 
    uint64_t a; 
    uint32_t b; 
}; 

class Y 
{ 
    uint16_t c; 
}; 

class XY : public X, public Y 
{ 
    uint16_t d; 
}; 

int main() { 
    printf("sizeof(X) is %zu\n", sizeof(X)); 
    printf("sizeof(Y) is %zu\n", sizeof(Y)); 
    printf("sizeof(XY) is %zu\n", sizeof(XY)); 
} 

आउटपुट: उदाहरण के लिए:

sizeof(X) is 16 
sizeof(Y) is 2 
sizeof(XY) is 16 

ऊपर से पता चलता है कि मानक गद्दी अनुगामी वर्ग के लिए अनुमति देता है व्युत्पन्न वर्ग के सदस्यों के लिए प्रयोग की जाने वाली। कक्षा XY में दो अतिरिक्त uint16_t सदस्य हैं, फिर भी इसका आकार बेस क्लास X के आकार के बराबर है।

दूसरे शब्दों में, कक्षा XY लेआउट एक अन्य वर्ग के समान है जिसमें कोई आधार वर्ग नहीं है और XY के सभी सदस्यों को पते द्वारा आदेश दिया गया है, उदा। struct XY2 { uint64_t a; uint32_t b; uint16_t c; uint16_t d; };

यह गैर-मानक लेआउट क्या बनाता है कि व्युत्पन्न वर्ग का आकार आधार वर्गों और व्युत्पन्न वर्ग के सदस्यों के आकार का कार्य नहीं है।

ध्यान दें कि struct/class का आकार सबसे बड़ा संरेखण आवश्यकता वाले सदस्यों में से एक के संरेखण का एक बहु है। ताकि वस्तुओं के एक सरणी को ऐसे सदस्य के लिए उपयुक्त रूप से गठबंधन किया जा सके। अंतर्निहित प्रकारों के लिए आमतौर पर sizeof(T) == alignof(T)। इसलिए sizeof(X)sizeof(uint64_t) का एक से अधिक है।

मुझे यकीन है कि मानक struct के लिए विशेष उपचार की आवश्यकता है कि क्या नहीं हूँ, लेकिन g++-5.1.1 साथ अगर classstruct साथ बदल दिया है इसके बाद के संस्करण कोड अलग उत्पादन पैदावार:

sizeof(X) is 16 
sizeof(Y) is 2 
sizeof(XY) is 24 

दूसरे शब्दों में, पीछे गद्दी अंतरिक्ष अनुकूलन है उपयोग नहीं किया गया जब struct शामिल है (सटीक स्थितियों के लिए परीक्षण नहीं किया गया)।

संबंधित मुद्दे