क्या C ++ में बेहतर है मान द्वारा पास करें या निरंतर संदर्भ से गुजरें?


213

क्या C ++ में बेहतर है मान द्वारा पास करें या निरंतर संदर्भ से गुजरें?

मैं सोच रहा हूं कि कौन सा अभ्यास बेहतर है। मुझे लगता है कि निरंतर संदर्भ से गुजरने वाले को कार्यक्रम में बेहतर प्रदर्शन के लिए प्रदान करना चाहिए क्योंकि आप चर की प्रतिलिपि नहीं बना रहे हैं।


संबं धत
लं क

जवाबों:


203

यह आम तौर पर सबसे अच्छा अभ्यास की सिफारिश की जा करने के लिए इस्तेमाल 1 करने के लिए के लिए स्थिरांक रेफरी द्वारा उपयोग के पास सभी प्रकार , प्रकार (builtin के अलावा char, int, double, आदि), iterators के लिए और समारोह वस्तुओं के लिए (lambdas, से पाने कक्षाएं std::*_function)।

चाल शब्दार्थ के अस्तित्व से पहले यह विशेष रूप से सच था । कारण सरल है: यदि आप मूल्य से गुजरते हैं, तो वस्तु की एक प्रति बनाई जानी चाहिए और, बहुत छोटी वस्तुओं को छोड़कर, यह हमेशा एक संदर्भ पारित करने की तुलना में अधिक महंगा है।

C ++ 11 के साथ, हमने चाल शब्दार्थ हासिल किए हैं । संक्षेप में, शब्दार्थ को अनुमति दें कि, कुछ मामलों में, किसी वस्तु को बिना कॉपी किए "मूल्य से" पारित किया जा सकता है। विशेष रूप से, यह मामला है जब आप जिस वस्तु से गुजर रहे हैं, वह एक प्रतिद्वंद्विता है

अपने आप में, किसी वस्तु को स्थानांतरित करना अभी भी कम से कम उतना ही महंगा है जितना कि संदर्भ से गुजरना। हालांकि, कई मामलों में एक फ़ंक्शन आंतरिक रूप से किसी भी वस्तु की नकल करेगा - अर्थात यह तर्क का स्वामित्व लेगा । 2

इन स्थितियों में हमारे पास निम्नलिखित (सरलीकृत) व्यापार बंद हैं:

  1. हम ऑब्जेक्ट को संदर्भ द्वारा पास कर सकते हैं, फिर आंतरिक रूप से कॉपी कर सकते हैं।
  2. हम वस्तु को मूल्य से पास कर सकते हैं।

"मूल्य से गुजारें" तब भी वस्तु को कॉपी करने का कारण बनता है, जब तक कि वस्तु एक प्रतिद्वंद्विता न हो। एक प्रतिद्वंद्विता के मामले में, वस्तु को इसके बजाय स्थानांतरित किया जा सकता है, ताकि दूसरा मामला अचानक "कॉपी, फिर" नहीं हो, लेकिन "स्थानांतरित करें, फिर (संभावित) फिर से चलें"।

बड़ी वस्तुओं के लिए जो उचित चाल वाले कंस्ट्रक्टर (जैसे वैक्टर, स्ट्रिंग्स…) को लागू करते हैं, दूसरा मामला पहले की तुलना में बहुत अधिक कुशल होता है। इसलिए, फ़ंक्शन द्वारा तर्क का स्वामित्व लेने पर मान का उपयोग करने की अनुशंसा की जाती है , और यदि ऑब्जेक्ट प्रकार कुशल मूविंग का समर्थन करता है


एक ऐतिहासिक नोट:

वास्तव में, किसी भी आधुनिक संकलक को यह पता लगाने में सक्षम होना चाहिए कि मूल्य से गुजरना महंगा है या नहीं, और यदि संभव हो तो एक कॉन्स्ट रेफरी का उपयोग करने के लिए कॉल को रूपांतरित करें।

सिद्धांत रूप में। व्यवहार में, कंपाइलर हमेशा फ़ंक्शन के बाइनरी इंटरफ़ेस को तोड़ने के बिना इसे बदल नहीं सकते हैं। कुछ विशेष मामलों में (जब फ़ंक्शन को इनलाइन किया जाता है) तो प्रतिलिपि वास्तव में बढ़ाई जाएगी यदि कंपाइलर यह पता लगा सकता है कि फ़ंक्शन में क्रियाओं के माध्यम से मूल ऑब्जेक्ट को नहीं बदला जाएगा।

लेकिन सामान्य तौर पर कंपाइलर इसे निर्धारित नहीं कर सकते हैं, और C ++ में मूवमेंट शब्दार्थ के आगमन ने इस अनुकूलन को बहुत कम प्रासंगिक बना दिया है।


स्कॉट मेयर्स में 1 ईजी, प्रभावी सी ++

2 यह विशेष रूप से अक्सर ऑब्जेक्ट कंस्ट्रक्टर्स के लिए सच है, जो तर्क ले सकते हैं और उन्हें आंतरिक रूप से निर्मित ऑब्जेक्ट की स्थिति का हिस्सा बनने के लिए स्टोर कर सकते हैं।


हम्म् ... मुझे यकीन नहीं है कि यह रेफरी द्वारा पारित होने के लायक है। डबल-एस
सर्गटेक

3
हमेशा की तरह, बढ़ावा यहाँ मदद करता है। boost.org/doc/libs/1_37_0/libs/utility/call_traits.htm में टेम्पलेट प्रकार स्वचालित रूप से यह पता लगाने के लिए होता है कि एक प्रकार एक अंतर्निहित प्रकार (टेम्पलेट्स के लिए उपयोगी है, जहां आप कभी-कभी आसानी से नहीं जान सकते हैं)।
सीजरबी

13
यह उत्तर एक महत्वपूर्ण बिंदु याद करता है। स्लाइसिंग से बचने के लिए, आपको संदर्भ (पास या अन्यथा) से गुजरना होगा। देखें stackoverflow.com/questions/274626/...
ChrisN

6
@ क्रिस: सही है। मैंने बहुरूपता के पूरे हिस्से को छोड़ दिया क्योंकि यह पूरी तरह से अलग शब्दार्थ है। मेरा मानना ​​है कि ओपी (शब्दार्थ) का अर्थ था "मूल्य से" तर्क गुजरना। जब अन्य शब्दार्थों की आवश्यकता होती है, तो प्रश्न स्वयं भी सामने नहीं आता है।
कोनराड रुडोल्फ

98

संपादित करें: डेप अब्राहम द्वारा cpp-next पर नया लेख:

गति चाहिए? मूल्य से गुजारें।


उन मानों के लिए पास करें जहां नकल सस्ती है, अतिरिक्त लाभ है कि कंपाइलर मान सकता है कि ऑब्जेक्ट उर्फ ​​नहीं हैं (समान ऑब्जेक्ट नहीं हैं)। पास-दर-संदर्भ का उपयोग करके संकलक हमेशा ऐसा नहीं मान सकता। सरल उदाहरण:

foo * f;

void bar(foo g) {
    g.i = 10;
    f->i = 2;
    g.i += 5;
}

संकलक में यह अनुकूलन कर सकते हैं

g.i = 15;
f->i = 2;

चूंकि यह जानता है कि f और g समान स्थान साझा नहीं करते हैं। यदि जी एक संदर्भ (foo &) था, तो संकलक ऐसा नहीं मान सकता था। चूँकि gi तब f-> i द्वारा अलग किया जा सकता है और 7. का मान होना चाहिए, इसलिए संकलक को स्मृति से gi के नए मान को फिर से लाना होगा।

अधिक व्यावहारिक नियमों के लिए, यहां मूव कंस्ट्रक्टर्स लेख (अत्यधिक अनुशंसित पढ़ने) में पाए जाने वाले नियमों का एक अच्छा सेट है ।

  • यदि फ़ंक्शन साइड इफेक्ट के रूप में तर्क को बदलने का इरादा रखता है, तो इसे गैर-कॉन्स्टेंस संदर्भ द्वारा लें।
  • यदि फ़ंक्शन अपने तर्क को संशोधित नहीं करता है और तर्क आदिम प्रकार का है, तो इसे मूल्य से लें।
  • अन्यथा इसे कॉन्स्ट रेफरेंस द्वारा लें, सिवाय निम्नलिखित मामलों में
    • यदि फ़ंक्शन को फिर भी कॉन्स्ट रेफरेंस की एक कॉपी बनाने की आवश्यकता होती है, तो इसे मूल्य से लें।

ऊपर दिए गए "आदिम" का अर्थ है मूल रूप से छोटे डेटा प्रकार जो कुछ बाइट्स लंबे होते हैं और पॉलीमॉर्फिक (पुनरावृत्तियाँ, फ़ंक्शन ऑब्जेक्ट्स आदि ...) या कॉपी करने के लिए महंगे होते हैं। उस कागज में, एक नियम है। विचार यह है कि कभी-कभी कोई एक प्रतिलिपि बनाना चाहता है (यदि मामले में तर्क को संशोधित नहीं किया जा सकता है), और कभी-कभी कोई नहीं चाहता है (यदि कोई तर्क में अस्थायी रूप से वैसे भी था तो वह फ़ंक्शन में ही तर्क का उपयोग करना चाहता है। , उदाहरण के लिए)। पेपर विस्तार से बताता है कि यह कैसे किया जा सकता है। C ++ 1x में उस तकनीक का उपयोग मूल भाषा समर्थन के साथ किया जा सकता है। तब तक, मैं उपरोक्त नियमों के साथ जाऊंगा।

उदाहरण: एक स्ट्रिंग अपरकेस बनाने के लिए और अपरकेस संस्करण को वापस करने के लिए, किसी को हमेशा मान से गुजरना चाहिए: किसी को भी इसकी एक प्रति लेनी होगी (कोई व्यक्ति सीधे संदर्भ को नहीं बदल सकता है) - इसलिए इसे जितना संभव हो उतना बेहतर पारदर्शी बनाएं कॉल करने वाले और उस कॉपी को जल्दी बनाएं ताकि कॉल करने वाले उस पेपर में जितना संभव हो सके उतना अधिक अनुकूलन कर सकें:

my::string uppercase(my::string s) { /* change s and return it */ }

हालाँकि, अगर आपको किसी भी तरह से पैरामीटर बदलने की आवश्यकता नहीं है, तो इसे कॉन्स्टेंस के संदर्भ में लें:

bool all_uppercase(my::string const& s) { 
    /* check to see whether any character is uppercase */
}

हालांकि, यदि आप पैरामीटर का उद्देश्य तर्क में कुछ लिखना है, तो इसे गैर-कॉन्स्टेंस संदर्भ द्वारा पास करें

bool try_parse(T text, my::string &out) {
    /* try to parse, write result into out */
}

मुझे आपके नियम अच्छे लगे लेकिन पहले भाग के बारे में निश्चित नहीं हैं जहाँ आप इसे रेफ के रूप में पारित नहीं करने के बारे में बात करते हैं। हाँ, यकीन है, लेकिन कुछ के रूप में पारित नहीं कर रहा है बस के अनुकूलन के क्यूस बिल्कुल समझ में नहीं आता है। यदि आप उस स्टैक ऑब्जेक्ट को बदलना चाहते हैं जिसे आप पास कर रहे हैं, तो रेफ द्वारा ऐसा करें। यदि आप नहीं करते हैं, तो इसे मूल्य से पास करें। यदि आप इसे बदलना नहीं चाहते हैं, तो इसे कॉन्स्ट-रेफ के रूप में पास करें। पास-बाय-वैल्यू के साथ आने वाला अनुकूलन रेफरी के रूप में गुजरने के दौरान अन्य चीजों को प्राप्त करने के बाद से कोई फर्क नहीं पड़ता। मैं समझ नहीं "गति चाहते हैं?" अगर आप उन
ऑप्स पर

जोहान्स: जब मैंने इसे पढ़ा तो मुझे वह लेख बहुत पसंद आया, लेकिन जब मैंने इसकी कोशिश की तो मैं निराश हो गया। यह कोड GCC और MSVC दोनों पर विफल रहा। क्या मुझे कुछ याद था, या यह अभ्यास में काम नहीं करता है?
user541686

मुझे नहीं लगता कि मैं इस बात से सहमत हूं कि यदि आप किसी भी तरह से एक प्रतिलिपि बनाना चाहते हैं, तो आप इसे मान (कॉन्स्ट रेफरी के बजाय) पास करेंगे, फिर इसे स्थानांतरित करेंगे। इसे इस तरह से देखें, क्या अधिक कुशल है, एक कॉपी और एक चाल (यदि आप इसे आगे पास करते हैं तो आपके पास 2 प्रतियां भी हो सकती हैं), या बस एक कॉपी? हाँ, दोनों ओर कुछ विशेष मामले हैं, लेकिन यदि आपका डेटा वैसे भी स्थानांतरित नहीं किया जा सकता है (जैसे: पूर्णांक के टन के साथ एक POD), तो अतिरिक्त प्रतियों की कोई आवश्यकता नहीं है।
आयन टोडरेल

2
मेहरदाद, आप क्या उम्मीद करते हैं, यह निश्चित नहीं है, लेकिन कोड उम्मीद के
मुताबिक

मैं कंपाइलर को यह समझाने के लिए कॉपी करने की आवश्यकता पर विचार करता हूं कि प्रकार भाषा में कमी को पूरा नहीं करते हैं। मैं __restrict__अत्यधिक प्रतियों की तुलना में GCC's (जो संदर्भों पर भी काम कर सकता हूं) का उपयोग कर सकता हूं। बहुत खराब मानक C ++ ने C99 के restrictकीवर्ड को नहीं अपनाया है ।
रुस्लान

12

प्रकार पर निर्भर करता है। आप एक संदर्भ और अनुमापन बनाने के लिए छोटे उपरि जोड़ रहे हैं। डिफ़ॉल्ट प्रतिलिपि ctor का उपयोग करने वाले पॉइंटर्स के बराबर या छोटे आकार वाले प्रकारों के लिए, संभवतः मान से पास करना अधिक तेज़ होगा।


गैर-देशी प्रकारों के लिए, आप केवल संदर्भों के बजाय कॉन्स्ट रेफ़रेंस का उपयोग करके एक प्रदर्शन वृद्धि प्राप्त कर सकते हैं (यह इस बात पर निर्भर करता है कि कंपाइलर कितनी अच्छी तरह कोड का अनुकूलन करता है)।
ओ जे।

9

जैसा कि यह बताया गया है, यह प्रकार पर निर्भर करता है। अंतर्निहित डेटा प्रकारों के लिए, मूल्य द्वारा पास करना सबसे अच्छा है। यहां तक ​​कि कुछ बहुत छोटी संरचनाएं, जैसे कि जोड़े की एक जोड़ी मूल्य से गुजरकर बेहतर प्रदर्शन कर सकती हैं।

यहाँ एक उदाहरण है, मान लें कि आपके पास पूर्णांक मान है और आप इसे किसी अन्य दिनचर्या के लिए पास करना चाहते हैं। यदि उस मान को एक रजिस्टर में संग्रहीत करने के लिए अनुकूलित किया गया है, तो यदि आप इसे संदर्भ के रूप में पास करना चाहते हैं, तो इसे पहले मेमोरी में संग्रहीत किया जाना चाहिए और फिर कॉल को करने के लिए स्टैक पर रखी गई मेमोरी के लिए एक सूचक। यदि इसे मान से पास किया जा रहा है, तो जो आवश्यक है वह है स्टैक पर धकेल दिया गया रजिस्टर। (विवरण विभिन्न कॉलिंग सिस्टम और सीपीयू की तुलना में थोड़ा अधिक जटिल हैं)।

यदि आप टेम्प्लेट प्रोग्रामिंग कर रहे हैं, तो आप आमतौर पर कॉन्स्ट रेफ़र से हमेशा गुजरने के लिए मजबूर होते हैं क्योंकि आपको पता नहीं होता है कि किस प्रकार में पास किया जा रहा है। वैल्यू के हिसाब से किसी चीज़ को ख़राब करने के लिए पेनल्टी पास करना, बिल्ट-इन टाइप को पास करने के पेनाल्टी से बहुत बदतर है। const रेफरी द्वारा।


शब्दावली पर ध्यान दें: एक लाख ints वाली संरचना अभी भी एक "POD प्रकार" है। संभवतः आपके लिए इसका मतलब है 'बिल्ट-इन प्रकारों के लिए यह मूल्य से गुजरना सबसे अच्छा है'।
स्टीव जेसप

6

यह वह है जो मैं आम तौर पर एक गैर-टेम्पलेट फ़ंक्शन के इंटरफ़ेस को डिजाइन करते समय काम करता हूं:

  1. मान द्वारा पास करें यदि फ़ंक्शन पैरामीटर को संशोधित नहीं करना चाहता है और मान कॉपी करने के लिए सस्ता है (int, double, float, char, bool, etc ...) ध्यान दें कि std :: string, std :: वेक्टर, और बाकी मानक पुस्तकालय में कंटेनरों की संख्या नहीं है)

  2. यदि मूल्य कॉपी करने के लिए महंगा है और फ़ंक्शन इंगित किए गए मान को संशोधित नहीं करना चाहता है तो NAL पॉइंटर से पास करें और NULL एक ऐसा मान है जिसे फ़ंक्शन हैंडल करता है।

  3. यदि गैर-संकेंद्र सूचक द्वारा पास करें यदि मान कॉपी करना महंगा है और फ़ंक्शन इंगित किए गए मान को संशोधित करना चाहता है और NULL एक मान है जिसे फ़ंक्शन संभालता है।

  4. मान को कॉपी करने के लिए महंगा होने पर कॉन्स्ट रेफरेंस से गुजरें और फ़ंक्शन निर्दिष्ट मूल्य को संशोधित नहीं करना चाहता है और NULL एक वैध मान नहीं होगा यदि एक पॉइंटर का उपयोग किया गया था।

  5. मान को कॉपी करने के लिए महंगा होने पर नॉन-कास्ट संदर्भ द्वारा पास करें और फ़ंक्शन निर्दिष्ट मूल्य को संशोधित करना चाहता है और NULL एक वैध मान नहीं होगा यदि एक पॉइंटर का उपयोग किया गया था।


std::optionalतस्वीर में जोड़ें और आपको अब पॉइंटर्स की आवश्यकता नहीं है।
वायलेट जिराफ

5

लगता है जैसे आपको अपना जवाब मिल गया। मूल्य द्वारा पास करना महंगा है, लेकिन यदि आपको इसकी आवश्यकता है तो आपको काम करने की एक प्रति देता है।


मुझे यकीन नहीं है कि इसे क्यों वोट दिया गया था? यह मुझे समझ में आता है। यदि आप वर्तमान में संग्रहीत मूल्य की आवश्यकता करने जा रहे हैं, तो मूल्य से गुजरें। यदि नहीं, तो संदर्भ पास करें।
टोटी

4
यह पूरी तरह से निर्भर है। संदर्भ द्वारा POD (सादा पुराना डेटा) प्रकार करना वास्तव में अधिक मेमोरी एक्सेस के कारण प्रदर्शन को कम कर सकता है।
टॉर्लैक

1
स्पष्ट रूप से संदर्भ द्वारा int पास करना कुछ भी नहीं बचाता है! मुझे लगता है कि यह प्रश्न उन चीजों का तात्पर्य करता है जो एक सूचक से बड़ी हैं।
GeekyMonkey

4
यह स्पष्ट नहीं है, मैंने बहुत से लोगों द्वारा कोड देखा है जो वास्तव में यह नहीं समझते हैं कि कंप्यूटर कैसे कॉस्ट रेफरी द्वारा सरल चीजों को पारित करने का काम करते हैं क्योंकि उन्हें बताया गया है कि ऐसा करना सबसे अच्छी बात है।
टॉरलैक

4

कॉन्स्ट रेफरेंस से गुजरने वाला नियम बेहतर है। लेकिन अगर आपको स्थानीय स्तर पर फ़ंक्शन तर्क को संशोधित करने की आवश्यकता है, तो आपको मूल्य से गुजरने का बेहतर उपयोग करना चाहिए। कुछ बुनियादी प्रकारों के लिए सामान्य रूप से मूल्य और संदर्भ से गुजरने के लिए दोनों का प्रदर्शन समान है। वास्तव में आंतरिक रूप से सूचक द्वारा दर्शाया गया संदर्भ, यही कारण है कि आप उदाहरण के लिए उम्मीद कर सकते हैं कि सूचक के लिए दोनों पासिंग के मामले में समान हैं, या यहां तक ​​कि मूल्य से गुजरना अनावश्यक आवश्यकता के कारण तेजी से हो सकता है।


यदि आपको पैरामीटर के कैली की कॉपी को संशोधित करने की आवश्यकता है, तो आप मूल्य से गुजरने के बजाय कॉल कोड में एक प्रतिलिपि बना सकते हैं। IMO आपको आमतौर पर कार्यान्वयन विवरण के आधार पर API का चयन नहीं करना चाहिए जैसे: कॉलिंग कोड का स्रोत वैसे ही होता है, लेकिन उसका ऑब्जेक्ट कोड नहीं है।
स्टीव जेसप

यदि आप मूल्य से गुजरते हैं तो प्रतिलिपि बनाई जाती है। और IMO का कोई मतलब नहीं है कि आप किस तरह से एक प्रति बनाते हैं: तर्क के माध्यम से मूल्य या स्थानीय स्तर पर - यह वही है जो सी ++ की चिंता करता है। लेकिन डिजाइन के दृष्टिकोण से मैं आपसे सहमत हूं। लेकिन मैं केवल यहाँ सी ++ सुविधाओं का वर्णन करता हूं और डिजाइन को नहीं छूता।
नागरटेक

1

अंगूठे के एक नियम के रूप में, गैर-वर्ग प्रकारों के लिए मूल्य और वर्गों के लिए कास्ट संदर्भ। यदि कोई वर्ग वास्तव में छोटा है तो मूल्य से गुजरना बेहतर है, लेकिन अंतर न्यूनतम है। आप वास्तव में जो बचना चाहते हैं, वह कुछ विशाल वर्ग को मूल्य से पारित कर रहा है और इसे सभी डुप्लिकेट कर रहा है - यदि आप गुजर रहे हैं तो इससे बहुत बड़ा अंतर होगा, कहते हैं, एक std :: वेक्टर जिसमें कुछ तत्व हैं।


मेरी समझ यह है कि std::vectorवास्तव में ढेर पर अपनी वस्तुओं को आवंटित किया जाता है और वेक्टर ऑब्जेक्ट खुद कभी नहीं बढ़ता है। अरे रुको। यदि ऑपरेशन वेक्टर की एक प्रतिलिपि बनाने का कारण बनता है, हालांकि, यह वास्तव में सभी तत्वों को जाने और डुप्लिकेट करेगा। यह बुरा होगा।
स्टीवन लू

1
हां, यही मैं सोच रहा था। sizeof(std::vector<int>)स्थिर है, लेकिन मूल्य द्वारा इसे पारित करना अभी भी किसी भी संकलक की अनुपस्थिति में सामग्री की नकल करेगा।
पीटर

1

छोटे प्रकारों के लिए मूल्य से गुजरें।

बड़े प्रकारों के लिए कॉन्स्टिट्यूशन पास (बड़ी की परिभाषा मशीनों के बीच भिन्न हो सकती है) लेकिन, B ++ में, C ++ 11 में, मान से पास करें यदि आप डेटा का उपभोग करने जा रहे हैं, क्योंकि आप मूवमेंट शब्दार्थ का फायदा उठा सकते हैं। उदाहरण के लिए:

class Person {
 public:
  Person(std::string name) : name_(std::move(name)) {}
 private:
  std::string name_;
};

अब कॉलिंग कोड करेगा:

Person p(std::string("Albert"));

और केवल एक ऑब्जेक्ट बनाया जाएगा और name_कक्षा में सीधे सदस्य में स्थानांतरित किया जाएगा Person। यदि आप कॉन्स्टिट्यूशन से गुजरते हैं, तो इसे डालने के लिए एक कॉपी बनानी होगी name_


-5

सरल अंतर: - फ़ंक्शन में हमारे पास इनपुट और आउटपुट पैरामीटर होते हैं, इसलिए यदि आपका पासिंग इनपुट और आउट पैरामीटर समान है, तो संदर्भ द्वारा कॉल का उपयोग करें यदि इनपुट और आउटपुट पैरामीटर अलग-अलग हैं, तो मूल्य द्वारा कॉल का उपयोग करना बेहतर है।

उदाहरण void amount(int account , int deposit , int total )

इनपुट पैरामीटर: खाता, जमा आउटपुट पैरामोटर: कुल

इनपुट और आउट वाउल द्वारा अलग-अलग उपयोग कॉल है

  1. void amount(int total , int deposit )

इनपुट कुल जमा आउटपुट कुल

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.