我正在尝试 SFINAE,并尝试获取有关“类函数”对象(函数、函数指针、lambda 以及任何重载括号运算符的内容)的信息,作为练习。我已将代码精简为以下内容:
#include <iostream>
#include <functional>
#include <string>
#include <type_traits>
template<typename Callable, typename = void>
struct function_type_info;
template<typename R, typename G, typename... T>
struct function_type_info<R(G::*)(T...)> {
using return_type = R;
};
template<typename C>
struct function_type_info<C, typename std::enable_if<!std::is_member_function_pointer<C>::value, C>::type> {
using return_type = typename function_type_info<decltype(&C::operator())>::return_type;
};
struct Callback {
int operator()(double, std::string) {
return 0;
}
};
int main(int argc, char** argv) {
auto b = [](double, std::string) -> int { return 0; };
auto c = Callback{};
std::cout << std::boolalpha;
std::cout << std::is_same<function_type_info<decltype(&Callback::operator())>::return_type, int>::value << std::endl;
std::cout << std::is_same<function_type_info<decltype(b)>::return_type, int>::value << std::endl;
std::cout << std::is_same<function_type_info<decltype(c)>::return_type, int>::value << std::endl;
return 0;
}
编译器给我以下错误:
<source>:32:64: error: incomplete type 'function_type_info<main(int, char**)::<lambda(double, std::string)> >' used in nested name specifier
32 | std::cout << std::is_same<function_type_info<decltype(b)>::return_type, int>::value << std::endl;
| ^~~~~~~~~~~
<source>:32:80: error: template argument 1 is invalid
32 | std::cout << std::is_same<function_type_info<decltype(b)>::return_type, int>::value << std::endl;
| ^
<source>:33:64: error: incomplete type 'function_type_info<Callback>' used in nested name specifier
33 | std::cout << std::is_same<function_type_info<decltype(c)>::return_type, int>::value << std::endl;
| ^~~~~~~~~~~
<source>:33:80: error: template argument 1 is invalid
33 | std::cout << std::is_same<function_type_info<decltype(c)>::return_type, int>::value << std::endl;
我不明白为什么会出现此错误。对于 lambda 和 Callback 对象,应接受
function_type_info
的第二个特化:std::is_member_function_pointer<C>::value
为 false,因此 !std::is_member_function_pointer<C>::value
为 true,因此 std::enable_if<!std::is_member_function_pointer<C>::value, C>::type
应计算为 C
并且模板推导应成功。
如果我将
std::is_member_function_pointer<C>::value, C>::type
更改为 std::is_member_function_pointer<C>::value, void>::type
那么适用于 Callback
,但不适用于 lambda。再次,我不明白为什么这个改变首先使它适用于 Callback
,以及为什么如果它适用于 Callbacks
,它不适用于 lambda。 lambda 的错误如下:
<source>: In instantiation of 'struct function_type_info<main(int, char**)::<lambda(double, std::string)> >':
<source>:32:62: required from here
<source>:16:11: error: invalid use of incomplete type 'struct function_type_info<int (main(int, char**)::<lambda(double, std::string)>::*)(double, std::__cxx11::basic_string<char>) const, void>'
16 | using return_type = typename function_type_info<decltype(&C::operator())>::return_type;
| ^~~~~~~~~~~
<source>:7:8: note: declaration of 'struct function_type_info<int (main(int, char**)::<lambda(double, std::string)>::*)(double, std::__cxx11::basic_string<char>) const, void>'
7 | struct function_type_info;
| ^~~~~~~~~~~~~~~~~~
<source>: In function 'int main(int, char**)':
<source>:32:80: error: template argument 1 is invalid
32 | std::cout << std::is_same<function_type_info<decltype(b)>::return_type, int>::value << std::endl;
就好像我不被允许引用 lambda 的括号运算符...
注意:我知道我可以通过其他方式获取返回类型信息(
std::result_of
/std::invoke_result
),但正如我所说,我这样做是为了练习。
主要模板使用
template<typename Callable, typename = void>
struct function_type_info;
专业定义为:
template<typename C>
struct function_type_info<C, typename std::enable_if<!std::is_member_function_pointer<C>::value, C>::type> {
using return_type = typename function_type_info<decltype(&C::operator())>::return_type;
};
错误在于提供
C
作为 std::enable_if
的第二个模板参数。
您只使用 function_type_info
的特化,其中第二个参数是 void
,而不是 C
。