আমি কীভাবে একটি ওভারলোডেড ফাংশনটিতে একটি পয়েন্টার নির্দিষ্ট করব?


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()সংকলকটি পুনরাবৃত্তকারী ধরণের মাধ্যমে সমাধান করবে। স্পষ্টতই, এটি (জিসিসি 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()কোনও শ্রেণির সদস্য হওয়ার কারণে (উপরের সম্পাদিত উদাহরণটি দেখুন)
ডাভকা

9
@ থেড্রো: দ্বিতীয় পদ্ধতিটি আসলে অনেক বেশি নিরাপদ, যদি ওভারলোডগুলির মধ্যে একটি প্রথম পদ্ধতিটি নিঃশব্দে অপরিবর্তিত আচরণ দেয় তবে দ্বিতীয়টি সংকলনের সময় সমস্যাটি ধরা দেয়।
বেন ভয়েগট

3
@ বেনওয়েগ্ট হুম, আমি এটি vs2010-এ পরীক্ষা করে দেখেছি এবং কম্পাইল করার সময় স্থির_কাস্ট সমস্যাটি ধরবে না এমন কোনও মামলা খুঁজে পাইনি। এটি "টার্গেটের সাথে এই নামের সাথে ফাংশনের কোনওটিই নয়" দিয়ে একটি সি 2440 দিয়েছে। আপনি কি স্পষ্ট করতে পারেন?
নাথন মন্টিলিওন

5
@ নাথান: সম্ভবত আমি ভাবছিলাম reinterpret_cast। বেশিরভাগ ক্ষেত্রে আমি সি-স্টাইলের কাস্টগুলি এর জন্য ব্যবহৃত দেখি। আমার নিয়মটি হ'ল ফাংশন পয়েন্টারগুলিতে কাস্টগুলি বিপজ্জনক এবং অপ্রয়োজনীয় (দ্বিতীয় কোড স্নিপেট শো হিসাবে, একটি অন্তর্নিহিত রূপান্তর বিদ্যমান)।
বেন ভয়েগট 21

3
সদস্য ফাংশনগুলির জন্য:std::for_each(s.begin(), s.end(), static_cast<void (A::*)(char)>(&A::f));
সাম-ডব্লু

29

ল্যাম্বদাসের উদ্ধার! (দ্রষ্টব্য: সি ++ 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); });

পলিমারফিক ল্যাম্বডাস (সি ++ 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);
}

লাম্বদাসের জন্য হুররে! আসলে, ওভারলোড রেজোলিউশন সমস্যার একটি দুর্দান্ত সমাধান solution (আমি এটিও ভেবেছিলাম, তবে জলের জলে কাদা না দেওয়ার জন্য এটি আমার উত্তর থেকে ছেড়ে দেওয়ার সিদ্ধান্ত নিয়েছি))
অলডো

একই ফলাফলের জন্য আরও কোড। আমার মনে হয় ল্যাম্বডাস এর জন্য তৈরি করা হয়নি।
টোমা জ্যাটো - মনিকা

@ টম্যাজাটো পার্থক্যটি হ'ল এই উত্তরটি কার্যকর হয়, এবং স্বীকৃত উত্তরটি ব্যবহার করে না (উদাহরণস্বরূপ ওপি পোস্ট করেছেন - আপনাকেও ব্যবহার করতে হবে mem_fnএবং bindকোনটি বিটিডাব্লুও রয়েছে সি ++ 11)। এছাড়াও, যদি আমরা সত্যিই পেডেন্টিক পেতে চাই তবে [&](char a){ return f(a); }এটি 28 টি অক্ষর এবং static_cast<void (A::*)(char)>(&f)35 টি অক্ষর।
সহস্রাব্দ

1
@ টোম্যাজাতো সেখানে আপনি coliru.stacked- ক্রুকড.com/a/1faad53c4de6c233 এটিকে আরও কীভাবে পরিষ্কার করবেন তা নিশ্চিত নন
সহস্রাব্দ

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
@ দাভকা বোরিং আমরা যা চাই তা হল। এছাড়াও, পুনরাবৃত্তকারীরা সাধারণত অপ [ব্যবহার করার চেয়ে দ্রুততর (ধীর হতে পারে) নয়) তবে এটি যদি আপনার উদ্বেগ হয়।

7
লুপগুলির জন্য অ্যালগোরিদমগুলিকে অগ্রাধিকার দেওয়া উচিত, যেহেতু তারা কম ত্রুটিযুক্ত এবং অপটিমাইজেশনের জন্য আরও ভাল সুযোগ থাকতে পারে। সে সম্পর্কে কোথাও একটি নিবন্ধ আছে ... এটি এখানে: drdobbs.com/184401446
অ্যাশলেস ব্রাইন

5
@ অ্যাশলি যতক্ষণ না "কম ত্রুটি প্রবণ" সম্পর্কে কিছু উদ্দেশ্যমূলক পরিসংখ্যান না দেখি আমি এটি বিশ্বাস করার প্রয়োজন দেখি না। এবং নিবন্ধের মায়ারগুলি পুনরুক্তি ব্যবহারকারী লুপগুলি নিয়ে কথা বলে মনে হচ্ছে - আমি যে লুপগুলির পুনরাবৃত্তি ব্যবহার করি না তার দক্ষতার কথা বলছি - আমার নিজস্ব মানদণ্ডগুলি অনুকূলিতকরণের সময় এগুলি সামান্য দ্রুততর হওয়ার প্রস্তাব দেয় - অবশ্যই কোনও ধীর গতি নেই।

1
আমি এখানে আছি, আমি আপনার সমাধানটি আরও ভালভাবে খুঁজে পাই।
পিটারহ - মনিকা

5

এখানে সমস্যাটি ওভারলোড রেজোলিউশনের নয় বরং টেম্পলেট প্যারামিটার ছাড়ের বলে মনে হচ্ছে । যদিও @ ইন সিলিকোর দুর্দান্ত উত্তরটি সাধারণভাবে একটি অস্পষ্ট ওভারলোডিং সমস্যার সমাধান করবে, তবে 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

আপনি যদি সি ++ 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/IGj9dKmSyx4J


ওপির সমস্যাটি কোনও ফাংশন টেম্পলেটে একটি ওভারলোড হওয়া নামটি পাস করতে সক্ষম হচ্ছে না এবং আপনার সমাধানটিতে কোনও ওভারলোড হওয়া নামটি কোনও ফাংশন টেম্পলেটে পাস করা জড়িত? এটি ঠিক একই সমস্যা is
ব্যারি

1
@ ব্যারি একই সমস্যা নয়। এই ক্ষেত্রে টেমপ্লেট আর্গুমেন্ট কর্তন সফল হয়। এটি কাজ করে (কয়েকটি ছোট ছোট টুইট সহ)।
Oktalist

@ অক্টালিস্ট যেহেতু আপনি সরবরাহ করছেন R, এটি হ্রাস করা হয়নি। এই উত্তরে এর কোনও উল্লেখ নেই।
ব্যারি

1
@ ব্যারি আমি সরবরাহ করছি না R, আমি সরবরাহ করছি ArgsRএবং Tছাড় করা হয়। সত্য যে উত্তরটি উন্নত হতে পারে। ( Tযদিও আমার উদাহরণে এটির কোনও উদাহরণ নেই, কারণ এটি কোনও পয়েন্ট-টু-সদস্য নয়, কারণ এটি এতে কাজ করবে না std::for_each))
Oktalist
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.