2012-12-17 19 views
8

यह एक पैरामीटर के पैक से विस्तार बहस में सरणी-टू-सूचक क्षय को रोकने के लिए संभव है?पैरामीटर पैक विस्तार में सरणी क्षय को रोकने

उदाहरण के लिए:

#include <iostream> 

void foo() { 
    std::cout << "empty\n"; 
} 

template <typename T, typename... Rest> 
void foo(T &&t, Rest... rest) { 
    std::cout << "T, ...\n"; 
    foo(rest...); 
} 

template <typename... Rest> 
void foo(char *p, Rest... rest) { 
    std::cout << "char*, ...\n"; 
    foo(rest...); 
} 

template <int N, typename... Rest> 
void foo(char (&first)[N], Rest... rest) { 
    std::cout << "char[], ...\n"; 
    foo(rest...); 
} 

int main() { 
    char a[2], b[2], c[2]; 
    foo(a, b, c); 
} 

... आउटपुट:

char[], ... 
char*, ... 
char*, ... 
empty 

आप देख सकते हैं, पहली कॉल सरणी-आधारित अधिभार को जाता है, लेकिन बाद में कॉल करने के लिए जाना pointer- आधारित अधिभार। क्या सरणी-आधारित अधिभार पर जाने के लिए सभी कॉल प्राप्त करने का कोई तरीका है?

संबंधित: Problems specializing variable template function

+2

'std :: आगे (बाकी) ...'? – Yakk

उत्तर

7

आप rvalue संदर्भ द्वारा पैरामीटर पैक पास करना चाहते हैं:

void foo(char (&first)[N], Rest&&... rest) 
           ^^ 

तो कोड इस समग्र की तरह दिखता है:

#include <iostream> 

void foo() { 
    std::cout << "empty\n"; 
} 

template <typename T, typename... Rest> 
void foo(T &&t, Rest... rest) { 
    std::cout << "T, ...\n"; 
    foo(rest...); 
} 

template <typename... Rest> 
void foo(char *p, Rest... rest) { 
    std::cout << "char*, ...\n"; 
    foo(rest...); 
} 

template <int N, typename... Rest> 
void foo(char (&first)[N], Rest&&... rest) { 
    std::cout << "char[], ...\n"; 
    foo(rest...); 
} 

int main() { 
    char a[2], b[2], c[2]; 
    foo(a, b, c); 
} 

देते परिणाम:

char[], ... 
char[], ... 
char[], ... 
empty 

मैंने ऐसा करने के लिए अन्य ओवरलोड को नहीं बदला है, लेकिन आप आमतौर पर उन्हें एक रावल्यू संदर्भ का उपयोग करना चाहते हैं (यदि वे वास्तव में उपयोग किए जा रहे थे)।

संपादित करें: तुम क्यों ऐसा करना चाहते हैं, उस पर के रूप में/कारण है कि यह काम करता है: एक rvalue संदर्भ या तो एक rvalue या एक lvalue करने के लिए बाध्य कर सकते हैं। यहां महत्वपूर्ण महत्वपूर्ण बिंदु यह है कि जब यह एक लालसा से बंधे होते हैं, तो यह एक अंतराल बनी हुई है। किसी सरणी के मामले में, यह अपनी पहचान को सरणी के रूप में बरकरार रखता है, इसलिए प्राप्त किया जाता है एक सरणी है।

जब/अगर हम मान द्वारा एक सरणी गुजरती हैं, यह सामान्य एक सूचक को "क्षय" से होकर गुजरती है, सिर्फ एक सामान्य समारोह के साथ की तरह।

इस विशिष्ट मामले के लिए, हम सामान्य लैवल्यू संदर्भ का भी उपयोग कर सकते हैं - लेकिन अगर हमने ऐसा किया है, तो किसी भी प्रकार के लिए काम नहीं करेगा जो एक लाभा नहीं था। उदाहरण के लिए, अगर हम foo(1,2,3); कॉल करने की कोशिश, हमें एक त्रुटि क्योंकि एक lvalue संदर्भ 1, 2 या 3 करने के लिए बाध्य नहीं कर सकते हैं मिल चाहते हैं। कि हम से निपटने के लिए एक const lvalue संदर्भ दे सकते हैं, लेकिन फिर हम rvalue करने के लिए सीधे संदर्भ बाध्यकारी नहीं होगा - हम rvalue कि पारित किया गया था की एक प्रति युक्त एक अस्थायी बनाने होगी, और फिर बंधन बदले में उस अस्थायी प्रति के लिए लालू संदर्भ। एक int के विशिष्ट मामले के लिए, शायद यह एक बड़ी समस्या नहीं होगी, लेकिन कुछ ऐसी चीज के साथ जो कॉपी करने के लिए अधिक महंगी थी (या अगर हम मूल तक पहुंच चाहते थे, न कि प्रतिलिपि) जो कि एक समस्या हो सकती है।

+1

"लेकिन आप आमतौर पर उन्हें एक रावल्यू संदर्भ का उपयोग करना चाहते हैं" मैं ऐसा क्यों करना चाहता हूं? यह काम क्यों करता है और ओपी नहीं करता है? –

+0

@ ओलाफडिएत्शे: संपादित। –

+1

इस उत्कृष्ट स्पष्टीकरण के लिए बहुत बहुत धन्यवाद। –

5

@ JerryCoffin का जवाब पहले से ही जगह मारा है, लेकिन मैं एक छोटी सी टिप्पणी जोड़ने चाहता था। आप इस तरह आइटम एक से सूची प्रसंस्करण के कोड को अलग कर सकते हैं:

void foo_list() { 
    std::cout << "empty\n"; 
} 

template <typename T, typename... Rest> 
void foo_list(T &&t, Rest&&... rest) { 
    foo(t); 
    foo_list(rest...); 
} 

template <int N> 
void foo(char (&t)[N]){ 
    // ... 
} 

void foo(char *){ 
    // ... 
} 

// etc... 

(हो सकता है कि वहाँ पहले से ही उस के लिए एक मुहावरा है?)।

+0

यह एक उत्कृष्ट बिंदु है। –

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