2016-08-25 3 views
15

मुझे कोई सराहना होगी कि कोई मुझे एक मजेदार की छोटी प्रतिलिपि बनाने की क्षमता के बारे में संकेत दे सकता है (लैम्बडा का उपयोग किया जाना है)। जैसा कि this question में बताया गया है, यह कार्यान्वयन है कि लैम्ब्डा तुच्छ रूप से प्रतिलिपि बनाने योग्य है या नहीं। उदाहरण के लिए इस प्रश्न के अंत में दिखाए गए कोड के लिए जीसीसी (5.4) और एमएसवीसी (2015) दोनों आग लगते हैं कि इन्हें असाधारण रूप से असाइन करने योग्य प्रतिलिपि नहीं है।त्रिकोणीय प्रतिलिपि के लिए परीक्षण करने के लिए कैसे परीक्षण करें lambdas

मुझे उम्मीद है कि structthis पॉइंटर रखते हुए और प्रत्येक कैप्चर किए गए मान (यदि कोई हो) की प्रतिलिपि रखते हुए इन प्रकार के लैम्ब्डा का प्रतिनिधित्व किया जाए। इसलिए वे सभी तुच्छ रूप से प्रतिलिपि बनाने योग्य प्रतीत होते हैं - कम से कम उस मामले के लिए जब कब्जे वाले मूल्यों को तुलनीय रूप से कॉपी करने योग्य होते हैं।

मेरा असली उपयोग केस इस सवाल को चला रहा है कि मैं एक कॉलबैक (std::function का आवृत्ति संस्करण जो आवंटित नहीं करता है और बहुत सरल मज़ेदारों के लिए है) का उपयोग कर रहा है जो एक निश्चित बफर रखता है जिसमें यह प्रतिलिपि बनाता है (जगह में) पारित डॉक्टर फिर मुझे उम्मीद है कि इन कॉलबैक को कॉपी/असाइन करने में सक्षम होने के लिए, लेकिन इसके लिए काम करने के लिए (बॉक्स के बाहर) इन निश्चित बफर की सरल याद-प्रतिलिपि उनके अंदर रखे गए मकसदों को कॉपी/असाइन करने के बराबर होनी चाहिए। test_functor() में

  1. कल्पना कीजिए कि नीचे मैं new नियुक्ति जैसे कार्य करें:

    तो मैं दो प्रश्न हैं जैसे

    new (&buffer) F(functor)

    यह नीचे दिखाया गया lambdas के प्रकार के लिए सिर्फ memcopy इस बफर करने के लिए सुरक्षित है? मुझे उम्मीद है कि ऐसा इसलिए होना चाहिए क्योंकि सभी मामलों के लिए केवल this पॉइंटर कैप्चर किया गया है या कैप्चर किए गए मानों को तुलनीय रूप से कॉपी करने योग्य हैं, लेकिन यह अच्छा होगा अगर कोई इसकी पुष्टि कर सके।

  2. मैं कैसे परीक्षण कर सकता हूं कि स्मृति की सरल प्रतिलिपि जहां मज़ेदार रखा जाता है वह मज़ेदार की प्रतिलिपि के बराबर है? यदि पहले प्रश्न का उत्तर सकारात्मक है तो std::is_trivially_copy_assignable सही उत्तर नहीं है।

#include <type_traits> 

template <typename F> 
void test_functor(const F& functor) 
{ 
    static_assert(std::is_trivially_destructible<F>::value, 
        "Functor not trivially destructible"); 
    static_assert(std::is_trivially_copy_constructible<F>::value, 
        "Functor not trivially copy constructible"); 
    static_assert(std::is_trivially_copy_assignable<F>::value, 
        "Functor not trivially copy assignable"); 
} 

struct A 
{ 
    void test() { test_functor([this]() { }); } 
}; 

struct B 
{ 
    void test() { test_functor([this](int v) { value = v; }); } 

    int value; 
}; 

struct C 
{ 
    void test(int v) { test_functor([=]() { value = v; }); } 

    int value; 
}; 

int main() 
{ 
    A a; 
    B b; 
    C c; 

    a.test(); 
    b.test(); 
    c.test(1); 

    return 0; 
} 
+2

आपको 'is_trivially_copyable' का उपयोग करना चाहिए, न कि' is_trivially_meow_constructible' या 'is_trivially_meow_assignable' है। Lambdas कभी भी असाधारण रूप से कॉपी (या चाल) असाइन करने योग्य नहीं हैं क्योंकि उनकी प्रति असाइनमेंट ऑपरेटर हटा दिया गया है; जो उन्हें आवश्यक रूप से कॉपी करने योग्य नहीं होने से रोकता है। –

+0

@ टी.सी. - क्या आप उत्तर अनुभाग में अपनी टिप्पणी कर सकते हैं? मैं इसे एक जवाब के रूप में चिह्नित करना चाहता हूं क्योंकि आपने सिर पर नाखून मारा है। मुझे पता नहीं था कि लैम्बडास के लिए कॉपी असाइनमेंट हटा दिया गया है। और वास्तव में 'is_trivially_copyable' गुज़र रहा है। धन्यवाद। – AndrzejO

उत्तर

6

कोई यह सुरक्षित नहीं है। यदि कंपाइलर कहता है कि कुछ को छोटी रूप से कॉपी नहीं किया जा सकता है, तो यह नहीं हो सकता है।

यह काम कर सकता है। लेकिन यह काम करने का मतलब यह नहीं है कि यह सुरक्षित है।

भले ही यह आज काम करता है, लेकिन कल यह संकलक अद्यतनों के बाद काम करना बंद कर देता है।

फिक्स बहुत आसान है। एक एसबीओ प्रकार (छोटे बफर ऑप्टिमाइज़ेशन) लिखें जिसे कम से कम कॉम्पैक्टिव की आवश्यकता नहीं है।

template<std::size_t S, std::size_t A> 
struct SBO { 
    void(*destroy)(SBO*) = nullptr; 
// void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr; 
    void(*move_ctor)(SBO* src, SBO* dest) = nullptr; 
    std::aligned_storage_t< S, A > buffer; 

    void clear() { 
    auto d = destroy; 
    destroy = nullptr; 
    // copy_ctor = nullptr; 
    move_ctor = nullptr; 
    if (d) d(this); 
    } 

    template<class T, class...Args> 
    T* emplace(Args&&... args) { 
    static_assert(sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment"); 
    T* r = new((void*)&buffer) T(std::forward<Args>(args)...); 
    destroy = [](SBO* buffer) { 
     ((T*)&buffer->buffer)->~T(); 
    }; 
    // do you need a copy ctor? If not, don't include this: 
    //copy_ctor = [](SBO const* src, SBO* dest) { 
    // auto s = (T const*)&src.buffer; 
    // dest->clear(); 
    // dest->emplace<T>(*s); 
    //}; 
    move_ctor = [](SBO* src, SBO* dest) { 
     auto* s = (T*)&src->buffer; 
     dest->clear(); 
     dest->emplace<T>(std::move(*s)); 
     src->clear(); 
    }; 
    return r; 
    } 
    SBO() = default; 
    SBO(SBO&& o) { 
    if (o.move_ctor) { 
     o.move_ctor(&o, this); 
    } 
    } 
    SBO& operator=(SBO&& o) { 
    if (this == &o) return *this; // self assign clear, which seems surprising 
    if (o.move_ctor) { 
     o.move_ctor(&o, this); 
    } 
    return *this; 
    } 
    // do you need a copy ctor? If so, implement `SBO const&` ctor/assign 
}; 

live example

अब यहां पंचलाइन है। std::function लगभग निश्चित रूप से आपके लिए यह पहले से ही करता है।

std::function में एक नो-थ्रो चाल के साथ एक छोटा प्रकार रखें और पूछें कि क्या निर्माण फेंक सकता है या नहीं। मैं अनुमान लगा रहा हूं कि आपके कार्यान्वयन में एसबीओ का उपयोग उस प्रकार को स्टोर करने के लिए किया जाएगा।

एमएसवीसी 2015 मुझे लगता है कि लैम्ब्डा के लिए दो std::string एस भंडारण के लिए पर्याप्त जगह है।

चीजों को सही करने के लिए उपरांत मामूली (दो पॉइंटर्स, और थोड़ा संकेत) है। आप अधिक इंडेक्सक्शन की लागत पर प्रति माह एक पॉइंटर प्रति स्टोरेज लागत को छोड़ सकते हैं (तालिका को "मैनुअल वीटेबल" में फैक्ट्री फ़ंक्शन में एक स्थिर स्थानीय के रूप में संग्रहीत करें: मैं उदाहरणों के लिए एक लिंक प्रदान कर सकता हूं यदि यह नहीं है एक बल्ब को प्रकाश दें), लेकिन 2 मिटाए गए तरीकों के साथ-साथ उन्हें स्थानीय रूप से स्टोर कर सकते हैं (3+ पर स्थिर तालिका पर विचार करें) जब तक कि प्रीमियम प्रीमियम पर न हो।

आप पहले से ही "मिटाना" आविष्कार कर रहे हैं, जो मूल रूप से एक फ़ंक्शन पॉइंटर को संग्रहीत करने की आवश्यकता है, जोड़ना (और शायद प्रतिलिपि) जोड़ना और नष्ट करना उतना अधिक ओवरहेड नहीं है।

+0

आपके उदाहरण के लिए धन्यवाद। मैं इसे देख लूंगा, लेकिन इस बीच में मैं उन चीज़ों को रखूंगा जो वे हैं (मैं 'std :: function' का परीक्षण कर रहा था - जीसीसी 4.9 के साथ मुझे लगता है - और यह लैम्बडास के लिए आवंटित कर रहा था)। कृपया टी.सी. द्वारा टिप्पणी देखें - जो आपके पहले वाक्य से बहुत अधिक मेल खाता है :) - मुझे 'std :: is_trivially_copyable' के माध्यम से छोटी प्रतिलिपि बनाने के लिए परीक्षण करना चाहिए। सबसे अच्छा संबंध – AndrzejO

+0

@AndrzejO हंसी! मैंने यह खो दिया। मुझे लगा कि संकलक बेवकूफ का थोड़ा सा था। फिर भी, अगर यह बेवकूफ़ है, तो आप यह नहीं कहते कि "मुझे सच में पता है कि क्या हो रहा है", जो मेरा मुद्दा था। ;) – Yakk

+0

याक, मैंने आपके उत्तर को समाधान के रूप में चिह्नित करने का निर्णय लिया है क्योंकि एमएसवीसी अभी भी दावा कर रहा है कि इस तरह के लैम्ब्स छोटे पैमाने पर प्रतिलिपि नहीं हैं। मैं कंपाइलर में त्रुटि पर सट्टा कर रहा हूं लेकिन ऐसे मामले में मेरे पास इसके द्वारा कॉपी करने योग्य बनाने के अलावा कोई विकल्प नहीं है। – AndrzejO

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