programing

C ++ 11은 std :: function 또는 lambda 함수가 관련된 경우 유형을 추론하지 않습니다.

coolbiz 2021. 1. 15. 08:31
반응형

C ++ 11은 std :: function 또는 lambda 함수가 관련된 경우 유형을 추론하지 않습니다.


이 함수를 정의 할 때

template<class A>
set<A> test(const set<A>& input) {
    return input;
}

test(mySet)템플릿 유형을 명시 적으로 정의하지 않고도 코드의 다른 곳에서 호출 할 수 있습니다 . 그러나 다음 기능을 사용하는 경우 :

template<class A>
set<A> filter(const set<A>& input,function<bool(A)> compare) {
    set<A> ret;
    for(auto it = input.begin(); it != input.end(); it++) {
        if(compare(*it)) {
            ret.insert(*it);
        }
    }
    return ret;
}

이 함수를 사용하여 호출 filter(mySet,[](int i) { return i%2==0; });하면 다음 오류가 발생합니다.

오류 : 'filter (std :: set &, main () : :)'호출에 일치하는 함수가 없습니다.

그러나 다음 버전 은 모두 작동합니다.

std::function<bool(int)> func = [](int i) { return i%2 ==0; };
set<int> myNewSet = filter(mySet,func);

set<int> myNewSet = filter<int>(mySet,[](int i) { return i%2==0; });

set<int> myNewSet = filter(mySet,function<bool(int)>([](int i){return i%2==0;}));

람다 함수를 직접 만들지 않고 식 안에 직접 넣을 때 C ++ 11이 템플릿 유형을 추측 할 수없는 이유는 무엇 std::function입니까?

편집하다:

주석에있는 Luc Danton의 조언에 따라, 여기에 템플릿을 명시 적으로 전달할 필요가없는 이전 기능의 대안이 있습니다.

template<class A,class CompareFunction>
set<A> filter(const set<A>& input,CompareFunction compare) {
    set<A> ret;
    for(auto it = input.begin(); it != input.end(); it++) {
        if(compare(*it)) {
            ret.insert(*it);
        }
    }
    return ret;
}

set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; });템플릿이 없어도에서 호출 할 수 있습니다 .

컴파일러는 새로운 decltype 키워드와 새로운 함수 반환 유형 구문을 사용하여 반환 유형을 어느 정도 추측 할 수도 있습니다. 다음은 하나의 필터링 함수와 값을 기반으로 키를 생성하는 하나의 함수를 사용하여 집합을 맵으로 변환하는 예입니다.

template<class Value,class CompareType,class IndexType>
auto filter(const set<Value>& input,CompareType compare,IndexType index) -> map<decltype(index(*(input.begin()))),Value> {
    map<decltype(index(*(input.begin()))),Value> ret;
    for(auto it = input.begin(); it != input.end(); it++) {
        if(compare(*it)) {
            ret[index(*it)] = *it;
        }
    }
    return ret;
}

템플릿을 직접 사용하지 않고 다음과 같이 호출 할 수도 있습니다.

map<string,int> s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toString(i); });

문제는 람다의 특성에 있습니다. 표준에 따라 고정 된 속성 집합이있는 함수 개체이지만 함수 아닙니다 . 표준은 람다가 std::function<>정확한 유형의 인수와 상태가없는 경우 함수 포인터 로 변환 될 수 있다고 결정합니다 .

그러나 이것이 람다가 std::function함수 포인터도 아니라는 것을 의미하지는 않습니다 . 그들은 구현하는 고유 한 유형 operator()입니다.

반면 유형 추론은 변환없이 정확한 유형 만 추론합니다 (const / volatile 제한 제외). 람다가 아닌 있기 때문에 std::function컴파일러는 호출 유형을 추론 할 수 없습니다 filter(mySet,[](int i) { return i%2==0; });어떤로 std::function<>인스턴스화.

다른 예제와 마찬가지로 첫 번째 예제에서는 람다를 함수 유형으로 변환 한 다음 전달합니다. 컴파일러 std::function는가 동일한 유형의 rvalue (임시) 인 세 번째 예제에서와 같이 유형을 추론 할 수 있습니다 .

인스턴스화 유형 int을 템플릿에 제공하면 두 번째 작업 예제 인 추론이 작동하지 않습니다. 컴파일러는 유형을 사용한 다음 람다를 적절한 유형으로 변환합니다.


사건은 잊어 버려 분석하기에는 너무 복잡합니다.

이 간단한 예를 들어 보자 :

 template<typename T>
 struct X 
 {
     X(T data) {}
 };

 template<typename T>
 void f(X<T> x) {}

이제 다음 f과 같이 호출하십시오 .

 f(10); 

여기에서 당신은 생각하는 유혹 수도 T로 추론 할 것입니다 int 따라서 위의 함수 호출이 작동합니다. 음, 그렇지 않습니다. 간단하게하기 위해 다음과 같은 다른 생성자 가 있다고 상상해보십시오 int.

 template<typename T>
 struct X 
 {
     X(T data) {}
     X(int data) {} //another constructor
 };

이제 T내가 글을 쓸 때 무엇 을 추론해야 f(10)합니까? 음, T할 수 있는 유형.

다른 많은 경우가있을 수 있습니다. 예를 들어이 전문화를 취하십시오.

 template<typename T>
 struct X<T*>         //specialized for pointers
 {
    X(int data) {}; 
 };

Now what T should be deduced to for the call f(10)? Now it seems even harder.

It is therefore non-deducible context, which explains why your code doesn't work for std::function which is an identical case — just looks complex at the surface. Note that lambdas are not of type std::function — they're basically instances of compiler generated classes (i.e they're functors of different types than std::function).


If we have:

template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
  return lambda(2);
}

int r = myfunc([](int i) { return i + 1; });

It will not compile. But if you previously declare:

template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1));

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}

You can call your function with a lambda parameter without problem.

There are 2 new pieces of code here.

First, we have a function declaration which is only useful to return an old-style function pointer type, based on given template parameters:

template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};

Second, we have a function that takes a template argument to build our expected lambda type calling 'getFuncType':

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}

With the correct template parameters, now we can call the real 'myfunc'. Complete code will be:

template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
  return lambda(2);
}

template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}

int r = myfunc([](int i) { return i + 1; });

You can declare any overload for 'getFuncType' to match your lambda parameter. For example:

template <typename Func, typename Arg1, typename Arg2>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr, Arg2* arg2 = nullptr) -> decltype((*func)(*arg1, *arg2)) {};

ReferenceURL : https://stackoverflow.com/questions/9998402/c11-does-not-deduce-type-when-stdfunction-or-lambda-functions-are-involved

반응형