मैं एक ओवरलोड फ़ंक्शन को पॉइंटर कैसे निर्दिष्ट करूं?


137

मैं std::for_each()एल्गोरिथ्म को एक अतिभारित फ़ंक्शन पास करना चाहता हूं । उदाहरण के लिए,

class A {
    void f(char c);
    void f(int i);

    void scan(const std::string& s) {
        std::for_each(s.begin(), s.end(), f);
    }
};

मुझे उम्मीद है कि संकलक f()पुनरावृत्त प्रकार से हल करेगा। जाहिर है, यह (GCC 4.1.2) यह नहीं करता है। तो, मैं कैसे निर्दिष्ट कर सकता हूं जो f()मुझे चाहिए?


जवाबों:


137

आप फ़ंक्शन पॉइंटर प्रकार से निहित फ़ंक्शन हस्ताक्षर के अनुसार उपयोग static_cast<>()करने के लिए निर्दिष्ट करने के लिए उपयोग कर सकते हैं f:

// Uses the void f(char c); overload
std::for_each(s.begin(), s.end(), static_cast<void (*)(char)>(&f));
// Uses the void f(int i); overload
std::for_each(s.begin(), s.end(), static_cast<void (*)(int)>(&f)); 

या, आप यह भी कर सकते हैं:

// The compiler will figure out which f to use according to
// the function pointer declaration.
void (*fpc)(char) = &f;
std::for_each(s.begin(), s.end(), fpc); // Uses the void f(char c); overload
void (*fpi)(int) = &f;
std::for_each(s.begin(), s.end(), fpi); // Uses the void f(int i); overload

यदि fकोई सदस्य फ़ंक्शन है, तो आपको इस डॉ। डॉब के लेख में प्रस्तुत समाधान का उपयोग करने की आवश्यकता है mem_funया आपके मामले के लिए ।


1
धन्यवाद! मुझे अभी भी एक समस्या है, हालांकि, शायद इस तथ्य के कारण कि f()एक वर्ग का सदस्य है (ऊपर संपादित उदाहरण देखें)
davka

9
@the_drow: दूसरी विधि वास्तव में बहुत अधिक सुरक्षित है, यदि ओवरलोड में से एक चला जाता है तो पहली विधि चुपचाप अपरिभाषित व्यवहार देती है, जबकि दूसरा संकलन समय पर समस्या को पकड़ता है।
बेन वोइग्ट

3
@BenVoigt हम्म, मैंने इसका परीक्षण vs2010 पर किया और ऐसा मामला नहीं पाया जहां static_cast संकलन समय पर समस्या को न पकड़ सके। इसने C2440 के साथ "इस प्रकार के कार्यों में से कोई भी कार्यक्षेत्र लक्ष्य प्रकार से मेल नहीं खाता" दिया। क्या आप स्पष्ट कर सकते हो?
नाथन मोंटेलेओन

5
@ नथन: यह संभव है कि मैं सोच रहा था reinterpret_cast। अक्सर मैं सी-स्टाइल की कास्ट को इसके लिए इस्तेमाल करता हूं। मेरा नियम सिर्फ इतना है कि फंक्शन पॉइंट्स पर कास्ट खतरनाक और अनावश्यक हैं (जैसा कि दूसरा कोड स्निपेट दिखाता है, एक निहित रूपांतरण मौजूद है)।
बेन वोइग्ट

3
सदस्य कार्यों के लिए:std::for_each(s.begin(), s.end(), static_cast<void (A::*)(char)>(&A::f));
sam-w

29

बचाव के लिए लम्बोदर! (नोट: C ++ 11 आवश्यक)

std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });

या लैम्बडा पैरामीटर के लिए डिस्क्रिप्ट का उपयोग करना:

std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); });

बहुरूपी मेमने के साथ (C ++ 14):

std::for_each(s.begin(), s.end(), [&](auto a){ return f(a); });

या ओवरलोडिंग को हटाकर (केवल नि: शुल्क कार्यों के लिए काम करता है):

void f_c(char i)
{
    return f(i);
}

void scan(const std::string& s)
{
    std::for_each(s.begin(), s.end(), f_c);
}

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

समान परिणाम के लिए अधिक कोड। मुझे लगता है कि यह नहीं है कि मेमने के लिए क्या बनाया गया था।
टॉम ज़ातो -

@ TomášZato अंतर यह है कि यह उत्तर काम करता है, और स्वीकृत व्यक्ति ऐसा नहीं करता (उदाहरण के लिए ओपी द्वारा पोस्ट किया गया - आपको भी उपयोग करने की आवश्यकता है mem_fnऔर bind, जो, BTW। C ++ 11 भी हैं)। इसके अलावा, अगर हम वास्तव में पांडित्य प्राप्त करना चाहते हैं, तो [&](char a){ return f(a); }28 वर्ण हैं, और static_cast<void (A::*)(char)>(&f)35 वर्ण हैं।
मिलेनियमबग

1
@ TomášZato ये लीजिए coliru.stacked-crooked.com/a/1faad53c4de6c233 यकीन नहीं कि यह कैसे और अधिक स्पष्ट बनाने के लिए
milleniumbug

18

यह काम क्यों नहीं करता

मुझे उम्मीद है कि संकलक f()पुनरावृत्त प्रकार से हल करेगा। जाहिरा तौर पर, यह (gcc 4.1.2) ऐसा नहीं करता है।

अगर ऐसा होता तो बहुत अच्छा होता! हालाँकि, for_eachएक फ़ंक्शन टेम्प्लेट है, जिसे निम्न के रूप में घोषित किया गया है:

template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );

टेम्पलेट कटौती UnaryFunctionको कॉल के बिंदु पर एक प्रकार का चयन करने की आवश्यकता है । लेकिन fएक विशिष्ट प्रकार नहीं है - यह एक अतिभारित कार्य है, fविभिन्न प्रकारों के साथ प्रत्येक एस है। ऐसा करने के लिए for_eachटेम्पलेट कटौती प्रक्रिया की सहायता के लिए कोई मौजूदा तरीका नहीं fहै, जो यह चाहता है, इसलिए टेम्पलेट कटौती बस विफल हो जाती है। टेम्पलेट कटौती सफल होने के लिए, आपको कॉल साइट पर अधिक काम करने की आवश्यकता है।

इसे ठीक करने के लिए सामान्य समाधान

कुछ वर्षों में यहाँ और बाद में C ++ 14 में होपिंग। उपयोग करने के बजाय एक static_cast(जो टेम्पलेट कटौती को "फिक्सिंग" द्वारा सफल होने की अनुमति देगा, जिसे fहम उपयोग करना चाहते हैं, लेकिन आपको मैन्युअल रूप से "एक" को "ठीक" करने के लिए अधिभार संकल्प करने की आवश्यकता है, हम हमारे लिए संकलक का काम करना चाहते हैं। हम fकुछ आर्गन्स पर कॉल करना चाहते हैं । सबसे सामान्य तरीके से संभव है, कि:

[&](auto&&... args) -> decltype(auto) { return f(std::forward<decltype(args)>(args)...); }

यह टाइप करने के लिए बहुत कुछ है, लेकिन इस तरह की समस्या अक्सर परेशान होती है, इसलिए हम इसे एक मैक्रो (आह) में लपेट सकते हैं:

#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(func(std::forward<decltype(args)>(args)...)) { return func(std::forward<decltype(args)>(args)...); }

और फिर इसका उपयोग करें:

void scan(const std::string& s) {
    std::for_each(s.begin(), s.end(), AS_LAMBDA(f));
}

यह वही करेगा जो आप चाहते हैं कि संकलक ने किया - नाम पर fही अधिभार संकल्प निष्पादित करें और बस सही काम करें। यह fनिशुल्क कार्य या सदस्य कार्य की परवाह किए बिना काम करेगा ।


7

आपके प्रश्न का उत्तर देने के लिए नहीं, लेकिन क्या मैं केवल वही हूं जो पाता है

for ( int i = 0; i < s.size(); i++ ) {
   f( s[i] );
}

for_eachइस मामले में सिलिको द्वारा सुझाए गए विकल्प की तुलना में सरल और छोटा दोनों ?


2
शायद, लेकिन यह उबाऊ है :), अगर मैं [] ऑपरेटर से बचने के लिए
पुनरावृत्त

3
@ डावका बोरिंग वही है जो हम चाहते हैं। इसके अलावा, iterators आमतौर पर op का उपयोग करने की तुलना में अधिक तेज़ (धीमा) हो सकता है, अगर यह आपकी चिंता है।

7
लूप्स के लिए एल्गोरिदम को प्राथमिकता दी जानी चाहिए, क्योंकि वे कम त्रुटि वाले हैं और अनुकूलन के लिए बेहतर अवसर हो सकते हैं। उस पर कहीं एक लेख है ... यहाँ यह है: drdobbs.com/184401446
AshleysBrain

5
@ एशले जब तक मैं "कम एरर प्रोन" पर कुछ ऑब्जेक्टिव स्टैटस नहीं देख लेता, मुझे यह मानने की कोई जरूरत नहीं है। और लेख में मेयर्स लूप्स के बारे में बात करते हुए दिखाई देते हैं जो पुनरावृत्तियों का उपयोग करते हैं - मैं उन लूप्स की दक्षता के बारे में बात कर रहा हूं जो कि पुनरावृत्तियों का उपयोग नहीं करते हैं - मेरे स्वयं के बेंचमार्क यह सुझाव देते हैं कि अनुकूलित किए जाने पर ये थोड़ी तेजी से होते हैं - निश्चित रूप से धीमा नहीं।

1
यहां मैं हूं, मैं भी आपके समाधान को बेहतर तरीके से ढूंढता हूं।
पीटर - मोनिका

5

यहाँ समस्या अधिभार संकल्प नहीं है, लेकिन वास्तव में टेम्पलेट पैरामीटर कटौती लगता है । हालांकि @In सिलिको से उत्कृष्ट उत्तर सामान्य रूप से अस्पष्ट ओवरलोडिंग की समस्या को हल करेगा, यह सबसे अच्छा फिक्स लगता है जब इससे निपटने के लिए std::for_each(या समान) अपने टेम्पलेट मापदंडों को स्पष्ट रूप से निर्दिष्ट करना है :

// Simplified to use free functions instead of class members.

#include <algorithm>
#include <iostream>
#include <string>

void f( char c )
{
  std::cout << c << std::endl;
}

void f( int i )
{
  std::cout << i << std::endl;
}

void scan( std::string const& s )
{
  // The problem:
  //   error C2914: 'std::for_each' : cannot deduce template argument as function argument is ambiguous
  // std::for_each( s.begin(), s.end(), f );

  // Excellent solution from @In silico (see other answer):
  //   Declare a pointer of the desired type; overload resolution occurs at time of assignment
  void (*fpc)(char) = f;
  std::for_each( s.begin(), s.end(), fpc );
  void (*fpi)(int)  = f;
  std::for_each( s.begin(), s.end(), fpi );

  // Explicit specification (first attempt):
  //   Specify template parameters to std::for_each
  std::for_each< std::string::const_iterator, void(*)(char) >( s.begin(), s.end(), f );
  std::for_each< std::string::const_iterator, void(*)(int)  >( s.begin(), s.end(), f );

  // Explicit specification (improved):
  //   Let the first template parameter be derived; specify only the function type
  std::for_each< decltype( s.begin() ), void(*)(char) >( s.begin(), s.end(), f );
  std::for_each< decltype( s.begin() ), void(*)(int)  >( s.begin(), s.end(), f );
}

void main()
{
  scan( "Test" );
}

4

यदि आपको C ++ 11 का उपयोग करने में कोई आपत्ति नहीं है, तो यहां एक चतुर सहायक है जो स्थिर कलाकारों के समान (लेकिन उससे कम बदसूरत) है:

template<class... Args, class T, class R>
auto resolve(R (T::*m)(Args...)) -> decltype(m)
{ return m; }

template<class T, class R>
auto resolve(R (T::*m)(void)) -> decltype(m)
{ return m; }

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

सुझाव देने के लिए Miro Knejp के साथ धन्यवाद: https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKnSyx4J भी देखें


ओपी की समस्या एक फ़ंक्शन टेम्पलेट में एक अतिभारित नाम को पारित करने में सक्षम नहीं हो रही है, और आपके समाधान में एक फ़ंक्शन टेम्पलेट में एक अधिभार नाम पारित करना शामिल है? यह ठीक वैसी ही समस्या है।
बैरी

1
@ बेरी यही समस्या नहीं है। टेम्पलेट तर्क कटौती इस मामले में सफल होती है। यह काम करता है (कुछ मामूली tweaks के साथ)।
ओकेटलिस्ट

@Oktalist क्योंकि आप प्रदान कर रहे हैं R, यह घटाया नहीं है। इस उत्तर में इसका कोई उल्लेख नहीं है।
बैरी

1
@ बैरी मैं प्रदान नहीं कर Rरहा हूँ, मैं प्रदान कर रहा हूँ ArgsRऔर Tकाटा जाता है। यह सच है कि उत्तर को बेहतर बनाया जा सकता है। ( Tहालांकि मेरे उदाहरण में कोई नहीं है, क्योंकि यह एक पॉइंटर-टू-मेंबर नहीं है, क्योंकि इसके साथ काम नहीं होगा std::for_each।)
ओकटालिस्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.