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


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하거나 경우에, 사용 이 박사 Dobb의 기사에서 제시 한 솔루션을 .


1
감사! 그래도 여전히 문제가 있습니다. 아마도 f()클래스 멤버 라는 사실 때문일 것입니다 (위의 편집 된 예 참조)
davka

9
@the_drow : 두 번째 방법은 실제로 훨씬 안전합니다. 과부하 중 하나가 사라지면 첫 번째 방법은 자동으로 정의되지 않은 동작을 제공하지만 두 번째 방법은 컴파일 타임에 문제를 포착합니다.
벤 Voigt

3
@ BenVoigt 흠, 나는 이것을 vs2010에서 테스트했으며 static_cast가 컴파일 타임에 문제를 포착하지 못하는 경우를 찾을 수 없었습니다. "범위에이 이름을 가진 함수가 대상 유형과 일치하지 않습니다"라는 C2440을 제공했습니다. 당신은 명확히 할 수 있습니까?
Nathan Monteleone

5
@Nathan : 내가 생각하고 있었을 가능성이 reinterpret_cast있습니다. 가장 자주 사용되는 C 스타일 캐스트를 봅니다. 내 규칙은 함수 포인터에 대한 캐스트가 위험하고 불필요하다는 것입니다 (두 번째 코드 스 니펫이 보여 주듯이 암시 적 변환이 존재합니다).
Ben Voigt

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

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

람다를위한 만세! 실제로, 과부하 해결 문제에 대한 훌륭한 솔루션입니다. (나는 이것을 생각했지만 물을 흐릿하게하지 않기 위해 답을 내리지 않기로 결정했다.)
aldo

동일한 결과에 대한 더 많은 코드. 나는 그것이 람다를 위해 만들어진 것이 아니라고 생각합니다.
Tomáš Zato-복원 Monica Monica

@ TomášZato 차이점은이 답변이 효과가 있으며 허용되는 답변이 작동하지 않는다는 것입니다 (OP에 게시 된 예의 경우- mem_fnbindBTW도 C ++ 11 임). 또한 실제로 페탕 틱을 원한다면 [&](char a){ return f(a); }28 자 static_cast<void (A::*)(char)>(&f)이며 35 자입니다.
milleniumbug

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이 경우 silico에서 제안한 대안 보다 간단하고 짧 습니까?


2
아마, 그러나 그것은 지루합니다 :) 또한, [] 연산자를 피하기 위해 반복자를 사용하고 싶다면, 이것은 더 길어집니다 ...
davka

3
@Davka Boring은 우리가 원하는 것입니다. 또한 반복자가 관심사 인 경우 일반적으로 op [를 사용하는 것보다 빠르지 않습니다 (느려질 수 있음).

7
알고리즘은 오류 발생이 적고 최적화 기회가 더 높기 때문에 루프보다 선호됩니다. 어딘가에 대한 기사가 있습니다 ... 여기 있습니다 : drdobbs.com/184401446
AshleysBrain

5
@Ashley "오류가 덜 발생하기 쉬운"통계에 대한 객관적인 통계를 볼 때까지 믿지 않아도됩니다. 그리고 기사의 Meyers는 반복자를 사용하는 루프에 대해 이야기하는 것처럼 보입니다. 저는 반복자를 사용하지 않는 루프의 효율성에 대해 이야기하고 있습니다. 내 벤치 마크는 최적화 할 때 약간 느리다는 것을 암시하는 경향이 있습니다.

1
여기 있습니다. 귀하의 솔루션이 훨씬 더 좋습니다.
peterh-복원 모니카

5

여기서 문제는 과부하 해결이 아니라 실제로 템플릿 매개 변수 공제 인 것 같습니다 . @In silico 의 탁월한 답변 은 일반적으로 모호한 과부하 문제를 해결 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/IGj9dKmSyx4J 참조 .


OP의 문제는 오버로드 된 이름을 함수 템플릿으로 전달할 수 없으며 솔루션에 오버로드 된 이름을 함수 템플릿으로 전달하는 것이 문제입니까? 이것은 정확히 같은 문제입니다.
Barry

1
@Barry 같은 문제가 아닙니다. 이 경우 템플릿 인수 공제가 성공합니다. 작동합니다 (몇 가지 사소한 조정이 있습니다).
Oktalist

@Oktalist 제공하고 있기 때문에 R추론되지 않습니다. 이 답변에는 이에 대한 언급도 없습니다.
Barry

1
@Barry 제공하지 않습니다 . R제공하고 Args있습니다. RT추론된다. 대답이 향상 될 수 있다는 것은 사실입니다. ( T내 예제에는 포인터가 멤버가 아니기 때문에이 기능이 작동하지 않기 때문에 없습니다 std::for_each.)
Oktalist
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.