2016-05-14 101 views
4

我有一个很奇怪的问题。为了简单起见,可以说,我想有一个函数,它接受2个相同的函数声明作为参数Lambda作为模板函数

template<typename Func> 
void foo(Func a, Func b) 
{ 
    std::cout << "good"; 
} 

要尝试的事情了我带着putchar从cstdio,并创建了一个相同的功能相匹配的putchar

int myPutcharFunc(int) 
{ 
    return 0; 
} 

int main() 
{ 
    auto myPutcharLambda = [](int) -> int 
    { 
     return 0; 
    }; 

    foo(putchar, myPutcharFunc); // okay 
    foo(putchar, myPutcharLambda); //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>') 
} 

现在,lambda不想编译(关键是我想用lambda捕获)。

因此,让我们添加模板专业化,因为程序员比机器更聪明,对吧? :)

template<typename Func> 
void foo(Func a, Func b) 
{ 
    std::cout << "good"; 
} 

template<> 
void foo(int(*)(int), int(*)(int)) 
{ 
    std::cout << "good"; 
} 

没有运气,同样的错误 - 为什么? 但由于某些原因,当我注释掉模板特殊化:

//template<> 
void foo(int(*)(int), int(*)(int)) 
{ 
    std::cout << "good"; 
} 

代码编译。我显然不想为每一组函数的参数重载foo - 这就是模板的用途。每个步骤都使用msvC++和g ++进行测试。我究竟做错了什么?

回答

2

每个拉姆达是不同类型的,所以你需要有两个不同的模板参数,以获得他们

template<typename FuncA, typename FuncB> 
void foo(FuncA a, FuncB b) 

类型推断模板类型(见注释修正)时,不腐烂。所以一个lambda仍然是一个lambda,并不会衰减到一个函数指针。同样的原因,字符串文字被推断为char[N]而不是const char *

随着你的第二个例子使用专业化,它不想使用你的专业化,因为lambda不是一个函数指针。您可以将Lambda投射到函数指针并使其工作:https://godbolt.org/g/ISgPci技巧您可以在这里执行的是say + my_lambda,因为+是针对指针定义的,因此它会强制非捕获lambda成为函数指针。

+0

* “演绎模板类型时,类型不衰”。 *“相同的原因,字符串文字被推断为char [N]而不是const char *”*,原始字符串文字被推断为const char * –

+0

@PiotrSkotnicki它是什么时候作为char数组出现呢?我只是在其他一些问题上读到这个。 – xaxxon

+0

功能参数为参考类型时 –

2

两种可能性。

:只要把+在lambda前面:

foo(putchar, +myPutcharLambda); 

即工作,因为一元+期望的整数样值,诸如指针。因此,lambda转换为函数指针。

最终,(非捕获)lambda与函数指针不具有相同的类型,即使它愿意转换为函数指针。

编译器应该如何知道允许哪些转换生成相同类型的两个对象?

:还有另一种选择,利用的事实?:是愿意做一些转换,converting one type to another in some circumstances

template<typename Func1, typename Func2> 
void foo2(Func1 a, Func2 b) 
{ 
    using common_type = decltype(true?a:b); // or 'false', it doesn't matter 
    foo<common_type>(a,b); 
} 
+1

选项1特别调皮。 :) – erip

1

一个lambda都有自己的类型,可以衰减到一个函数指针,但不是一个模板函数匹配的情况下,它会为真正的功能,你发现,因为隐式转换。

在匹配模板的情况下,您需要消除歧义并使用所需的类型显式实例化foo,或将lambda转换为函数指针。 *当然,他们这样做

foo<decltype(putchar)>(putchar, myPutcharLambda); 

foo(putchar, +myPutcharLambda);