development

오버로드 된 함수에 대한 포인터를 어떻게 지정합니까?

big-blog 2020. 7. 4. 09:31
반응형

오버로드 된 함수에 대한 포인터를 어떻게 지정합니까?


과부하 된 함수를 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()원하는 것을 어떻게 지정할 수 있습니까?


함수 포인터 유형에 의해 암시 된 함수 서명에 따라 사용할 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하거나 경우에, 사용 이 박사 Dobb의 기사에서 제시 한 솔루션을 .


구조에 람다! (참고 : C ++ 11 필요)

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

또는 lambda 매개 변수에 decltype을 사용하십시오.

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);
}

왜 안돼?

컴파일러가 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무료 기능인지 멤버 기능 인지에 관계없이 작동 합니다.


귀하의 질문에 대답하지는 않지만 내가 찾은 유일한 사람입니까

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

both simpler and shorter than the for_each alternative suggested by in silico in this case?


The problem here seems to be not overload resolution but in fact template parameter deduction. While the excellent answer from @In silico will solve an ambiguous overloading problem in general, it seems the best fix when dealing with std::for_each (or similar) is to explicitly specify its template parameters:

// 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" );
}

If you don't mind using C++11, here's a clever helper that is similar to (but less ugly than) the static cast:

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; }

(Works for member functions; should be obvious how to modify it to work for freestanding functions, and you should be able to provide both versions and the compiler will select the right one for you.)

With thanks to Miro Knejp for suggesting: see also https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J.

참고URL : https://stackoverflow.com/questions/2942426/how-do-i-specify-a-pointer-to-an-overloaded-function

반응형