2012-05-19 5 views
0

나는 약간의 코드를 가지고 놀고 있으며 나는 약간의 것들에 대해 약간 당황 스럽다. 다음은 간단한 예입니다.C++ 11 - std :: function, templates and function objects, 이상한 문제

나는 더하기, 빼기 등의 산술 연산을 수행하는 Nodes을 가지고 있습니다. 내 프로그램에서 사용할 수있는 다양한 작업이 포함 된 컨테이너가 있습니다. 펑터를 변환

typedef double (*my_function)(std::vector<double>&, std::vector<Node*>&); 

그런 다음 다음과 같은 템플릿 함수를 사용

1) 함수 유형을 사용 :이 시점에서 두 가지 선택이있다, 지금

typedef std::binary_function<double, std::vector<double>&, std::vector<Node*>& > my_binary_function; 
auto const & product = [](double v, Node* n){ return v * n->GetEvaluation(); }; 
struct addition : public my_binary_function { 
double 
    operator()(std::vector<double>& weights, std::vector<Node*>& subtrees) { 
     return std::inner_product(weights.begin(), weights.end(), 
     subtrees.begin(), 0, std::plus<double>(), product); 
    } 
}; 

: 다음은 예입니다 :

template<typename F> typename F::result_type 
func(typename F::first_argument_type arg1, typename F::second_argument_type arg2) { 
    return F()(arg1, arg2); 
} 

2), 즉 std::function 래퍼 함수 유형을 사용 그래서

typedef std::function<double (std::vector<double>&, std::vector<Node*>&)> my_function; 

을 가지고 그것은 모든이 같은 것으로 요약된다 :

LoadDefaultFunctions() { 
    int minArity = 2; 
    int maxArity = 2; 
    function_set_.AddFunction("Add", func<addition> , minArity, maxArity, 1.0); // case 1 
OR 
    function_set_.AddFunction("Add", addition(), minArity, maxArity, 1.0); // case 2 

을 그리고 지금 문제 :

나는 방법 1 사용하는 경우), 나는이 컴파일 오류가 발생합니다 :

error: invalid initialization of non-const reference of type 
'std::binary_function<double, std::vector<double>&, 
std::vector<Node*>&>::result_type {aka std::vector<Node*>&}' 
from an rvalue of type 'double' 

템플릿을 변경하면 오류가 사라집니다 (인수가 실제로 이해가되지 않는 것을 확인하십시오).) :

template <typename F> typename F::first_argument_type 
func1(typename F::second_argument_type arg1, typename F::result_type arg2) { 
    return F()(arg1, arg2); 
} 

나는 때문에 같은 binary_op<double, double, double> 같은 다른 종류의, 아주 이상한 발견, 첫 번째 양식은 잘 작동합니다. 무슨 일 이니?

b) 1) 2)보다 빠릅니다 (작은 여백으로). 나는 아마 functor를 참조로 전달하거나 어떤 식 으로든 std::function을보다 효율적으로 감싸는 것을 가능케하는 몇 가지 깔끔한 트릭을 놓치고 있다고 생각하고있다. 어떤 아이디어?

c) 2)에서 typedef를 사용하지만 추가적으로 여전히 func을 사용하여 함수를 생성 한 후 std::function을 처리하면 여전히 2보다 빠릅니다. 즉,

누구나 내가이 모든 일의 역학을 이해하는 데 도움이된다면 정말 감사 할 것입니다.

감사합니다.

+4

'표준 :: result_of'를 호출하여 호출 가능한 엔티티의 결과 유형을 일반적으로 얻습니다. –

+1

'std :: binary_function'의 사용법은 약간 꺼져 있습니다 (매개 변수의 순서에 따라'result_type'이 메시지에서'double'이 아닌 것을 볼 수 있습니다). 그래도 그것을 고치기보다는 단순히 사용하지 않을 수 있습니까? KerrekSB가 지적한 것처럼'std :: result_of'를 선호하여'addition'의 클라이언트에서만 사용되어야하고'addition' 자체의 구현에서는 사용되지 않아야합니다. –

+0

고마워, 나는 그 부분을 고쳤다. binary_function이 더 이상 사용되지 않는다는 것을 알지 못했고, 보통 템플릿 매개 변수를 혼합하지 않았다. –

답변

2

b) 1) is faster than 2) (by a small margin). I'm thinking I'm probably missing some neat trick of passing the functor by reference or in some way that would enable std::function to wrap it more efficiently. Any ideas?

예, 1은 가상의 사용을 필요로 유형 - 삭제 (저장된 호출의 정확한 유형이 둘러싸 std::function 유형에없는)보다 빠른 2 std::function 수행으로 기대 함수 호출. 반면에 템플릿을 사용하면 정확한 유형을 알 수 있으므로 컴파일러는 호출을 인라인하는 기회가 더 많아 비용이 많이 드는 솔루션이됩니다. 이것은 당신이 functor를 어떻게 통과하는지와 관련이 없습니다.

오류가

템플릿 인수의 순서 올바르지 않습니다이다

typedef std::binary_function<double,     // arg1 
          std::vector<double>&, // arg2 
          std::vector<Node*>&  // return type 
          > my_binary_function; 

struct addition : public my_binary_function { 
    double           // return type 
    operator()(std::vector<double>& weights,  // arg1 
       std::vector<Node*>& subtrees)  // arg2 
    { ... 

binary_function에서 상속은 클래스에 어떤 형식 정의를 추가,하지만 사람들은 올바른 형식 정의되지 않습니다. 그런 다음 다음 템플리트에서 typedef를 사용할 때 유형이 일치하지 않습니다. 템플릿은 operator()std::vector<Node*>&을 반환 할 것을 기대하고, 그 func의 리턴 타입이지만, 당신이 펑터를 호출 할 때 당신이 얻을 것은 오류로 연결되는 double입니다 :

invalid initialization of ... std::vector<Node*>& from double

당신은 시도 할 수
+1

std :: function은 기술적으로 가상 호출을 필요로하지 않는다. GCC 구현과 (적어도 그것을 마지막으로 보았을 때) 구현 스토어는 호출 가능한 유형으로 인스턴스화 된 함수 템플리트에 대한 포인터를 지우기 전에 저장한다. 여전히 간접 참조가 있지만 반드시 가상 함수 호출은 아닙니다. –

+0

@JonathanWakely : 글쎄 ... 부스트 1.49에서는'virtual' 함수 호출이 아니고'vtable'이라는'function_base' 멤버를 통해 파견 된 호출입니다 ... 라이브러리는 자신의 가상 디스패치를 ​​구현하지만 어떤 요금, 유형 삭제 (및 상태 유지)를 수행 할 수 있으려면 * 반바지의 가상 디스패치 *가 필요합니다. –