2015-05-07 10 views
6

नीचे दिया गया कोड संकलित क्यों नहीं करता है?वैराडिक टेम्पलेट रिकर्सिव रिटर्न टाइप कटौती संकलन त्रुटि

template <typename T> 
T sum(T t){ 
    return t; 
} 

template <typename T, typename ...U> 
auto sum(T t, U... u) -> decltype(t + sum(u...)) { 
    return t + sum(u...); 
} 

int main() { 
    sum(1, 1.5, 2); 
} 

संकलन त्रुटि:

error: no matching function for call to ‘sum(int, double, int)’ 
sum(1, 1.5, 2); 

क्या इस सुविधा को लागू करने के लिए एक अच्छी युक्ति हो सकता है?

+1

मैंने आपके प्रश्न को अधिक न्यूनतम होने के लिए संपादित किया, और आपकी टिप्पणी से मेल खाता है। मैं भी कीवर्ड के साथ गड़बड़ कर रहा हूं (यह किसी और को एक ही समस्या खोजने में मदद कर सकता है) – Yakk

+0

ठीक है @ यक, धन्यवाद। – LunaticSoul

+0

संबंधित: http://stackoverflow.com/questions/29995642/create-n-dimensional-vector-with-given-sizes/30002714#30002714 – Barry

उत्तर

3

यहाँ हम सहायक प्रकार के काम को आगे:

namespace details { 
    template<class...Ts> 
    struct sum_t {}; 

    template<class T> 
    struct sum_t<T> { 
    T operator()(T t)const{ return std::forward<T>(t); } 
    }; 

    template<class T, class...Ts> 
    struct sum_t<T,Ts...> { 
    auto operator()(T t, Ts...ts)const 
    -> decltype(std::declval<T>() + sum_t<Ts...>{}(std::declval<Ts>()...)) 
    { 
     return std::forward<T>(t) + sum_t<Ts...>{}(std::forward<Ts>(ts)...); 
    } 
    }; 
} 

template<class...Ts> 
auto sum(Ts...ts) 
-> decltype(details::sum_t<Ts...>{}(std::declval<Ts>()...)) 
// -> std::result_of_t<details::sum_t<Ts...>(Ts...)> 
// above line is C++14 and cleaner version of previous line 
{ 
    return details::sum_t<Ts...>{}(std::forward<Ts>(ts)...); 
} 

बुनियादी समस्या यह थी कि एक टेम्पलेट समारोह अपने आप में नहीं देख सकते हैं जब एक में अपनी वापसी प्रकार की गणना -> decltype खंड।

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

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

template<class R0=void,class...Ts,class R=std::conditional_t< 
    !std::is_same<R0,void>{}, 
    R0, 
    std::result_of_t<details::sum_t<Ts...>(Ts...)> 
>> 
R sum(Ts...ts) 
{ 
    return details::sum_t<R, Ts...>{}(std::forward<Ts>(ts)...); 
} 

जहाँ मैं sum_t संशोधित पहले पैरामीटर के रूप वापसी प्रकार लेने के लिए:

namespace details { 
    template<class R,class...Ts> 
    struct sum_t { 
    R operator()()const{ return {}; } 
    }; 

    template<class R, class T> 
    struct sum_t<R, T> { 
    using R0 = std::conditional_t<!std::is_same<R,void>{},R,T>; 
    R0 operator()(T t)const{ return std::forward<T>(t); } 
    }; 

    template<class R, class T, class...Ts> 
    struct sum_t<R, T,Ts...> { 
    using R0 = std::conditional_t< 
     !std::is_same<R,void>{}, 
     R, 
     decltype(std::declval<T>() + sum_t<void,Ts...>{}(std::declval<Ts>()...)) 
    >; 
    R0 operator()(T t, Ts...ts)const 
    { 
     return std::forward<T>(t) + sum_t<void,Ts...>{}(std::forward<Ts>(ts)...); 
    } 
    }; 
} 

मुझे "लिखने के लिए इस योग करना सक्षम होना चाहते हैं बनाता है , लेकिन प्रत्येक उप-योग को R पर जारी रखने से पहले "या somesuch।

सी ++ 1z में, आप इसके बजाय एक फ़ोल्ड-एक्सप्रेशन का उपयोग करना चाहेंगे। R सेट करने में सक्षम होने के नाते अभी भी उपयोगी है, जैसे कि आप एक अभिव्यक्ति टेम्पलेट जोड़ रहे हैं, यह केवल वर्तमान स्कोप के अंत तक अभिव्यक्ति टेम्पलेट के रूप में मान्य हो सकता है।

सी ++ 14 में इस समस्या को ठीक करने के लिए, आपको R वापसी मान के साथ निरंतरता उत्तीर्ण शैली का उपयोग करना पड़ सकता है।

हम तो खेल में वापसी प्रकार कटौती गुना

Matrix m = sum(many_matrices...); 

(उदाहरण के लिए) Eigen में काम करने के लिए अनुमति देने के लिए कर सकता है।

जब आप पहली बार जेनेरिक कोड लिखना शुरू करते हैं, तो आपको खुद से पूछना होगा "खरगोश छेद कितना गहराई से जाना है?"

+1

ठोस उत्तर के लिए आसान +1, इच्छा है कि मैं "मुझे अधिक इंजीनियर जेनेरिक कोड पसंद है" के लिए एक और +1 दे सकता है। – Barry

+0

आपके पहले कोड स्निपेट की आखिरी पंक्ति में हमारे पास 'std :: आगे (ts) है ...'। आप 'std :: forward' का उपयोग क्यों कर रहे हैं जबकि' Ts ... 'सार्वभौमिक संदर्भ नहीं हैं (यानी, 'Ts && ...')? –

+0

@ कैसियो यह समाप्त होने से पहले एक मूल्य का अंतिम उपयोग है। यदि 'Ts' मान (संदर्भ नहीं) प्रकार हैं, तो हम' std :: move' को कुशल होने के लिए चाहते हैं। यदि वे अंतराल संदर्भ हैं, तो हम ** ** नहीं लेना चाहते हैं। यदि वे लालू संदर्भ हैं, तो हम आगे बढ़ना चाहते हैं। 'आगे' बिल्कुल यही करता है। उन्हें हमेशा मूल्य मानने के लिए प्रेरित किया जाएगा, लेकिन कोई व्यक्ति कार्य को स्पष्ट प्रकार से पारित कर सकता है, इस प्रकार मैं संभावना के खिलाफ सुरक्षा करता हूं। 'फॉरवर्ड' एक सशर्त कदम है, और यह मुझे आवश्यक कंडीशन से मेल खाता है। – Yakk

2

[basic.scope.pdecl] से उद्धरित:

The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any)

दूसरा समारोह टेम्पलेट की घोषणा अनुगामी वापसी प्रकार decltype(t + sum(u...)) के बाद पूरा हो गया है। इसलिए, decltype(t + sum(u...)) को पार्स करते समय, दूसरा टेम्पलेट अभी तक दायरे में नहीं है, और संकलक केवल पहला टेम्पलेट देख सकता है जो कॉल से मेल नहीं खाता है।

एक संभव फिक्स:

template <typename... T> 
struct ReturnType; 

template <typename T> 
struct ReturnType<T> { 
    typedef T Type; 
}; 

template <typename T, typename... U> 
struct ReturnType<T, U...> { 
    typedef typename ReturnType<U...>::Type Type_; 
    typedef decltype(std::declval<T>() + std::declval<Type_>()) Type; 
}; 

template <typename T> 
T sum(T t){ 
    return t; 
} 

template <typename T, typename ...U> 
typename ReturnType<T, U...>::Type sum(T t, U... u) { 
    return t + sum(u...); 
} 
+0

क्या आप एक फिक्स स्केच कर सकते हैं? (मुझे पता है कि ओपी ने एक के लिए नहीं पूछा था, लेकिन यह आपके जवाब में सुधार होगा!) – Yakk

+0

क्या एक अजीब व्यवहार! क्या मैं इस सामान्य योग समारोह को लागू करने के लिए कुछ कामकाज कर रहा हूं? – LunaticSoul

+0

यदि सी ++ 14 आपके लिए उपलब्ध है, तो पीछे की वापसी प्रकार को हटा दें, और सबकुछ ठीक होगा। – Lingxi

3

पूर्णता के हित में, के बाद से a similar question के इस संस्करण पर, Yakk पोस्ट टेम्पलेट विशेषज्ञता समाधान है कि मैं एक दूसरे में इस्तेमाल किया, मैं ADL समाधान है कि वह वहाँ इस्तेमाल किया प्रदान करेगा:

namespace N { 
    struct adl {}; 

    template <typename A, typename T> 
    T sum(A, T t){ 
     return t; 
    } 

    template <typename A, typename T, typename ...U> 
    auto sum(A a, T t, U... u) -> decltype(t + sum(a, u...)) { 
     return t + sum(a, u...); 
    } 
} 

template <typename... Args> 
auto sum(Args... args) -> decltype(sum(N::adl{}, args...)) 
{ 
    return sum(N::adl{}, args...); 
} 

कारण यह काम करता है कि sumN::sum के पीछे-रिटर्न-प्रकार में प्रयोग किया जाता एक आश्रित नाम है, और [temp.dep.res] से निम्नलिखित देखने नियम हैं:

In resolving dependent names, names from the following sources are considered:
(1.1) — Declarations that are visible at the point of definition of the template.
(1.2) — Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.

चूंकि लुकअप में घोषणाएं शामिल हैं जो परिभाषा पर परिभाषा संदर्भ, N::sum खुद को रिकर्सिव रूप से पा सकते हैं।

हालांकि, मैं यक के साथ सहमत हूं कि यह दृष्टिकोण अधिक भ्रमित है।

+0

क्या आपका मतलब है * परिभाषा के बिंदु और परिभाषा संदर्भ * या * तत्कालता और परिभाषा संदर्भ * के बिंदु पर? – Lingxi

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