当涉及std :: function或lambda函数时,C++ 11不推断类型

Dat*_*ore 54 c++ lambda templates c++11

当我定义这个功能时,

template<class A>
set<A> test(const set<A>& input) {
    return input;
}
Run Code Online (Sandbox Code Playgroud)

我可以使用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;
}
Run Code Online (Sandbox Code Playgroud)

当我使用此函数调用时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;}));
Run Code Online (Sandbox Code Playgroud)

为什么当我将lambda函数直接放在表达式中而不直接创建std::function?时,c ++ 11无法猜出模板类型?

编辑:

根据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;
}
Run Code Online (Sandbox Code Playgroud)

这可以通过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;
}
Run Code Online (Sandbox Code Playgroud)

它也可以在不使用模板的情况下调用,如

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

Dav*_*eas 61

问题在于lambdas的性质.它们是根据标准具有固定属性集的函数对象,但它们不是函数.该标准确定lambdas可以std::function<>使用确切的参数类型转换为,如果它们没有状态,则可以转换为函数指针.

但这并不意味着lambda std::function也不是函数指针.它们是实现的独特类型operator().

另一方面,类型推导只会推导出确切的类型,没有转换(const/volatile资格除外).因为lambda不是std::function编译器不能推断出调用中的类型:filter(mySet,[](int i) { return i%2==0; });要进行任何std::function<>实例化.

从其他示例开始,在第一个示例中,您将lambda转换为函数类型,然后传递它.编译器可以推导出那里的类型,如第三个例子中的那个,它std::function是相同类型的rvalue(临时).

如果您为int模板提供实例化类型,第二个工作示例,演绎没有发挥作用,编译器将使用该类型,然后将lambda转换为适当的类型.

  • 请注意,执行转换为`std :: function`的lambda不是,它的`std :: function`接受任何可调用的东西. (12认同)
  • 可以通过禁用"compare"arg的扣除来使其工作.我们可以使用这个签名:`template <class A> set <A> filter(const set <A>&input,typename NoOp <function <bool(A)>> :: type compare)`其中`Noop`是定义为`template <typename T> struct NoOp {typedef T type; };` (5认同)

Naw*_*waz 14

忘了你的情况.因为这太复杂了,无法进行分析.

举个简单的例子:

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

 template<typename T>
 void f(X<T> x) {}
Run Code Online (Sandbox Code Playgroud)

现在打电话给f:

 f(10); 
Run Code Online (Sandbox Code Playgroud)

在这里你可能会想到T会被推导出来int ,因此,上面的函数调用应该可行.嗯,事实并非如此.为了简单起见,想象一下还有另一个构造函数int:

 template<typename T>
 struct X 
 {
     X(T data) {}
     X(int data) {} //another constructor
 };
Run Code Online (Sandbox Code Playgroud)

现在T,当我写作时应该推断出什么f(10)?好吧,T可以任何类型.

请注意,可能还有许多其他此类情况.以此专业化为例,例如:

 template<typename T>
 struct X<T*>         //specialized for pointers
 {
    X(int data) {}; 
 };
Run Code Online (Sandbox Code Playgroud)

现在T应该推断出什么电话f(10)?现在看起来更难了.

因此它是不可导入的上下文,这解释了为什么你的代码不适用于std::function哪个是相同的情况 - 只是看起来很复杂.请注意,lambdas不是类型std::function- 它们基本上是编译器生成的类的实例(即它们是不同类型的函子std::function).


mar*_*nop 5

如果我们有:

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

int r = myfunc([](int i) { return i + 1; });
Run Code Online (Sandbox Code Playgroud)

它不会编译。但如果您之前声明:

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);
}
Run Code Online (Sandbox Code Playgroud)

您可以毫无问题地使用 lambda 参数调用您的函数。

这里有 2 段新代码。

首先,我们有一个函数声明,它仅用于根据给定的模板参数返回旧式函数指针类型:

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

其次,我们有一个函数,它采用模板参数来构建我们期望的 lambda 类型,调用“getFuncType”:

template <typename Func>
int myfunc(Func lambda)
{
  return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
Run Code Online (Sandbox Code Playgroud)

有了正确的模板参数,现在我们可以调用真正的“myfunc”。完整的代码将是:

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; });
Run Code Online (Sandbox Code Playgroud)

您可以为“getFuncType”声明任何重载以匹配您的 lambda 参数。例如:

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

  • 让我注意,我是从Boolinq项目[链接](https://github.com/k06a/boolinq)中学到这一点的 (2认同)