2015-06-17 8 views
9

एक भेदभाव संघ की खोज/टैग किए गए संस्करण मैं निष्कर्ष निकालना "नाशक संकलन समय पर कुछ शर्तों पर तुच्छ बनाने" के रूप में इस तरह के एक सुविधा में विशेष आवश्यकता नहीं है। मैं SFINAE किसी तरह का या की तरह (स्यूडोकोड) कुछ मतलब है:सशर्त तुच्छ नाशक

template< typename ...types > 
struct X 
{ 
    ~X() = default((std::is_trivially_destructible<types>{} && ...)) 
    { 
     // non-trivial code here 
    } 
}; 

इसका मतलब है कि अगर default(*) में हालत true है, तो नाशक की परिभाषा ~X() = default; के बराबर है, लेकिन अगर यह है false तो { // ... } शरीर बजाय प्रयोग किया।

#pragma once 
#include <type_traits> 
#include <utility> 
#include <experimental/optional> 

#include <cassert> 

template< typename ...types > 
class U; 

template<> 
class U<> 
{ 

    U() = delete; 

    U(U &) = delete; 
    U(U const &) = delete; 
    U(U &&) = delete; 
    U(U const &&) = delete; 

    void operator = (U &) = delete; 
    void operator = (U const &) = delete; 
    void operator = (U &&) = delete; 
    void operator = (U const &&) = delete; 

}; 

template< typename first, typename ...rest > 
class U< first, rest... > 
{ 

    struct head 
    { 

     std::size_t which_; 
     first value_; 

     template< typename ...types > 
     constexpr 
     head(std::experimental::in_place_t, types &&... _values) 
      : which_{sizeof...(rest)} 
      , value_(std::forward<types>(_values)...) 
     { ; } 

     template< typename type > 
     constexpr 
     head(type && _value) 
      : head(std::experimental::in_place, std::forward<type>(_value)) 
     { ; } 

    }; 

    using tail = U<rest...>; 

    union 
    { 

     head head_; 
     tail tail_; 

    }; 

    template< typename ...types > 
    constexpr 
    U(std::true_type, types &&... _values) 
     : head_(std::forward<types>(_values)...) 
    { ; } 

    template< typename ...types > 
    constexpr 
    U(std::false_type, types &&... _values) 
     : tail_(std::forward<types>(_values)...) 
    { ; } 

public : 

    using this_type = first; // place for recursive_wrapper filtering 

    constexpr 
    std::size_t 
    which() const 
    { 
     return head_.which_; 
    } 

    constexpr 
    U() 
     : U(typename std::is_default_constructible<this_type>::type{}, std::experimental::in_place) 
    { ; } 

    U(U &) = delete; 
    U(U const &) = delete; 
    U(U &&) = delete; 
    U(U const &&) = delete; 

    template< typename type > 
    constexpr 
    U(type && _value) 
     : U(typename std::is_same< this_type, std::decay_t<type> >::type{}, std::forward<type>(_value)) 
    { ; } 

    template< typename ...types > 
    constexpr 
    U(std::experimental::in_place_t, types &&... _values) 
     : U(typename std::is_constructible< this_type, types... >::type{}, std::experimental::in_place, std::forward<types>(_values)...) 
    { ; } 

    void operator = (U &) = delete; 
    void operator = (U const &) = delete; 
    void operator = (U &&) = delete; 
    void operator = (U const &&) = delete; 

    template< typename type > 
    constexpr 
    void 
    operator = (type && _value) & 
    { 
     operator std::decay_t<type> &() = std::forward<type>(_value); 
    } 

    constexpr 
    explicit 
    operator this_type &() & 
    { 
     assert(sizeof...(rest) == which()); 
     return head_.value_; 
    } 

    constexpr 
    explicit 
    operator this_type const &() const & 
    { 
     assert(sizeof...(rest) == which()); 
     return head_.value_; 
    } 

    constexpr 
    explicit 
    operator this_type &&() && 
    { 
     assert(sizeof...(rest) == which()); 
     return std::move(head_.value_); 
    } 

    constexpr 
    explicit 
    operator this_type const &&() const && 
    { 
     assert(sizeof...(rest) == which()); 
     return std::move(head_.value_); 
    } 

    template< typename type > 
    constexpr 
    explicit 
    operator type &() & 
    { 
     return static_cast< type & >(tail_); 
    } 

    template< typename type > 
    constexpr 
    explicit 
    operator type const &() const & 
    { 
     return static_cast< type const & >(tail_); 
    } 

    template< typename type > 
    constexpr 
    explicit 
    operator type &&() && 
    { 
     //return static_cast< type && >(std::move(tail_)); // There is known clang++ bug #19917 for static_cast to rvalue reference. 
     return static_cast< type && >(static_cast< type & >(tail_)); // workaround 
    } 

    template< typename type > 
    constexpr 
    explicit 
    operator type const &&() const && 
    { 
     //return static_cast< type const && >(std::move(tail_)); 
     return static_cast< type const && >(static_cast< type const & >(tail_)); 
    } 

    ~U() 
    { 
     if (which() == sizeof...(rest)) { 
      head_.~head(); 
     } else { 
      tail_.~tail(); 
     } 
    } 

}; 

// main.cpp 
#include <cstdlib> 

int 
main() 
{ 
    U< int, double > u{1.0}; 
    assert(static_cast<double>(u) == 1.0); 
    u = 0.0; 
    assert(static_cast<double>(u) == 0.0); 
    U< int, double > w{1}; 
    assert(static_cast<int>(w) == 1); 
    return EXIT_SUCCESS; 
} 

वर्ग U एक शाब्दिक प्रकार बनाने के लिए इस उदाहरण में यह U वर्ग (V) के रूप में लगभग एक ही परिभाषित करना संभव है (first, rest... के मामले में सभी तुच्छता से destructible कर रहे हैं), लेकिन एक नाशक की परिभाषा के बिना ~U (यानी शाब्दिक प्रकार है यदि सभी अवरोही प्रकार अक्षर हैं)। फिर टेम्पलेट प्रकार उर्फ ​​

template< typename ...types > 
using W = std::conditional_t< (std::is_trivially_destructible<types>{} && ...), V<types...>, U<types...> >; 

को परिभाषित करने और U और V दोनों में using tail = W<rest...>; को फिर से परिभाषित। इसलिए, दो लगभग समान वर्ग हैं, केवल विनाशक की उपस्थिति में भिन्न हैं। ऊपर दृष्टिकोण के लिए कोड की अत्यधिक डुप्लिकेशन की आवश्यकता है।

समस्या समस्या को कॉपी/चालित करने योग्य प्रकारों और operator = और std::is_trivially_copyable होने के लिए अन्य सभी शर्तों के साथ भी चिंतित है। 5 स्थितियां को लागू करने के लिए पूरी तरह से 2^5 संयोजन प्रदान करती हैं।

वहाँ तकनीक (और कम वर्बोज़, तो ऊपर वर्णित) का उपयोग करने के लिए उपस्थित सी ++ मैं याद आती है, या हो सकता है जल्द आ प्रस्ताव में व्यक्त किसी भी तैयार है?

विनाशक को constexpr के रूप में चिह्नित करने के लिए एक और विचारशील दृष्टिकोण (भाषा सुविधा) है और यह जांचने के लिए संकलक को प्रदान करता है कि शरीर तत्काल के दौरान तुच्छ एक के बराबर है या नहीं।

अद्यतन:

कोड सरलीकृत के रूप में टिप्पणी में कहा: unionunion तरह वर्ग बन गया। हटाए गए noexcept विनिर्देशक।

+1

मैं परिदृश्य जो नाशक की इस तरह की डिजाइन करने के लिए ले जा सकता है नहीं जानता। मैं यह भी महसूस करता हूं कि जो कुछ भी परिदृश्य है, जिसे आरएआईआई के उचित उपयोग के साथ हल किया जा सकता है (टेम्पलेट तर्कों के प्रकारों द्वारा लागू ... ')। – Nawaz

+1

मुझे नहीं लगता कि आप मूल्यों को संग्रहीत करने और सीधे विनाश का प्रबंधन करने के लिए 'X' क्यों चाहते हैं/क्यों चाहते हैं। शायद 'std :: is_trivially_destructible' मान पर एक सदस्य या आधार टेम्पलेट किया गया है, तो आप वांछित विनाश को पूर्ववत करने के लिए 'Member_Or_Base '' विनाशक का विशेषज्ञ कर सकते हैं। और पोस्ट करने के लिए यह बहुत अधिक कोड है - जब आपके पास कोई विशिष्ट समस्या होती है तो इसे कम से कम कोड बनाते हैं। –

+0

@ नवाज परिदृश्य तब उठता है जब हमें * सशर्त रूप से शाब्दिक * कक्षा को परिभाषित करने की आवश्यकता होती है।मैं पूरा उदाहरण और एक संभावित दोषपूर्ण समाधान प्रदान करता हूं। – Orient

उत्तर

1

सशर्त विनाशक को अतिरिक्त इंटरमीडिएट परत के माध्यम से टेम्पलेट विशेषज्ञता के साथ कार्यान्वित किया जा सकता है। उदाहरण के लिए:

Live Demo on Coliru

#include <type_traits> 
#include <iostream> 
#include <vector> 

using namespace std; 

template<typename T> 
class storage 
{ 
    aligned_storage_t<sizeof(T)> buf; 

    storage(storage&&) = delete; 
public: 
    storage() 
    { 
     new (&buf) T{}; 
    } 
    T &operator*() 
    { 
     return *static_cast<T*>(&buf); 
    } 
    void destroy() 
    { 
     (**this).~T(); 
    } 
}; 

template<typename T, bool destructor> 
struct conditional_storage_destructor 
{ 
    storage<T> x; 
}; 

template<typename T> 
struct conditional_storage_destructor<T, true> : protected storage<T> 
{ 
    storage<T> x; 

    ~conditional_storage_destructor() 
    { 
     x.destroy(); 
    } 
}; 

template<typename T> 
class wrapper 
{ 
    conditional_storage_destructor<T, not is_trivially_destructible<T>::value> x; 
public: 
    T &operator*() 
    { 
     return *(x.x); 
    } 
}; 

int main() 
{ 
    static_assert(is_trivially_destructible< wrapper<int> >::value); 
    static_assert(not is_trivially_destructible< wrapper<vector<int>> >::value); 

    cout << "executed" << endl; 
} 
+0

मुझे पता है। लेकिन यह कुछ विदेशी और अनावश्यक दिखता है। वैसे भी आपका उदाहरण * संघ-जैसी कक्षाओं * के साथ पूरी तरह से समस्या का समाधान कर सकते हैं। हां, बहुत verbose रास्ते में। – Orient

+0

@ ओरिएन्ट क्लास-स्तरीय 'स्थैतिक अगर 'सिंटैक्स में सुधार कर सकता है। आंद्रेई अलेक्जेंड्रेस्कू ने अपनी बातचीत में इसी तरह की स्थिति पर चर्चा की ["स्टेटिक अगर मैं एक हथौड़ा था"] (https://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Static-If-I-Had-a- हथौड़ा)। आईएसओ सी ++ के प्रस्तावों के कुछ 'स्थिर' हैं - उदाहरण के लिए [एन 332 9] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3329.pdf)। –

+0

दिलचस्प बात। – Orient

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