确定可调用对象是否具有模板参数
Determine if callable object has template arguments
是否可以确定可调用对象(函数、lambda表达式、函子等)的第一个参数是否依赖于模板参数?例如:(使用假设类型特征)
#include <iostream>
template <typename T>
void f1(T x) {}
void f2(int x) {}
int main() {
auto f3 = [](auto x) {};
std::cout << std::boolalpha
<< is_first_arg_generic<decltype(f1)>::value << std::endl
<< is_first_arg_generic<decltype(f2)>::value << std::endl;
<< is_first_arg_generic<decltype(f3)>::value << std::endl;
}
输出:
true
false
true
不,我认为这是不可能的。
函数模板f1
很难使用,因为在类型或表达式中几乎所有可能使用f1
的情况都必须立即将其转换为特定类型,只选择模板的一个专用化,否则会立即导致编译器错误。没有办法只传递模板名称,以便在SFINAE上下文中使用,因为模板模板参数只用于类模板和别名模板,而不是函数模板。
实际上,对于一组重载的非模板函数的名称,您也会遇到完全相同的问题。
但有一件事更接近于帮助:将所需的函数模板或重载函数封装在泛型lambda中。现在,它至少可以用作模板参数。问题就归结为计算出你的f3
。
如果给定的类型以某种方式已知或假定为闭包类型,那么很容易检测它是否是泛型lambda的类型:表达式&F::operator()
对非泛型lambda有效,对泛型lambda无效。此外,您可以检测是否可以使用参数类型的特定列表来调用任何可调用类型,和/或是否可以通过类型推导来专门化给定可调用类的operator()
,以生成参数类型的指定列表
但我认为这已经是我们所能做的了。实际上有无限多的可能的参数/参数类型列表。如果你能以某种方式将你关心的检查可能性限制在一个有限的列表中,你可以全部尝试。(或者,如果你可以选择一组不依赖于任意标识符的可计数可能性,你可以尽可能多地尝试编译器的最大实例化次数。)但这并不能真正解决问题。如果多个参数类型作为第一个参数有效,那么它仍然可以是一个非模板函数,其第一个参数类型可以从多个类型隐式转换。或者,即使您可以确定只有一个第一个参数类型有效,或者只有一个函数参数列表有效,您仍然可以有一个函数模板,其第一个参数是依赖类型,该依赖类型仅根据通过其他方式推导出的可能模板参数计算为一个特定类型。
理论上编译器可以知道。
但不是。f1
是一个模板,不是一个函数,甚至不是一个类型,所以不能将它传递给任何模板参数列表。
尽管可以确定函数类型的第一参数的类型。您可以尝试实例化模板两次,如果第一个参数的类型在两个模板实例之间不同,那么它就是一个模板化参数。当然,它通常不会适用于每种类型的模板(即,在一些模板中,对于两个不同的模板参数,参数可能是相同的类型)。
我做这个是为了我的用途(可能有更好的变体,也许Boost中有什么?):
#include <functional>
template< typename F >
struct function_traits;
template< typename Result, typename... Params >
struct function_traits< Result( Params... ) >{
static const size_t paramCount = sizeof...( Params );
using result = Result;
template< size_t i >
using param = typename std::tuple_element< i, std::tuple< Params... > >::type;
};
template< typename Result, typename... Params >
struct function_traits< Result(*)( Params... ) >
: public function_traits< Result( Params... ) > {
};
template< typename Result, typename... Params >
struct function_traits< std::function< Result( Params... ) > >
: public function_traits< Result( Params... ) >{
};
// shortcuts to help avoid the weird "typename" and "template" disambiguators
template< typename T >
using function_result_t = typename function_traits<T>::result;
template< typename T, size_t i >
using function_param_t = typename function_traits<T>::template param<i>; // lol, that's evil syntax
所以在这种情况下,我会像这样使用它:
using first_param_type_1st_try = function_param_t< f1<int>, 0 >;
using first_param_type_2nd_try = function_param_t< f1<unsigned>, 0 >;
bool is_first_param_probably_templated
= !std::is_same_v<first_param_type_1st_try, first_param_type_2nd_try>;
不过,这是毫无意义的,因为您知道非模板函数有非模板第一个参数。您必须将任何模板名称实例化为类型名称。
您可以使用SFINAE并将函数封装在测试模板中来解决其中的一些问题。再说一遍,这将是丑陋的,而且通常不会起作用。
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 如何检查给定的参数是否为 cv::noArray()?
- 如果返回 -1,时间() 的参数是否被修改?
- C++中大多数/所有 setter 函数的参数是否应该写为常量引用?
- 检查两个模板参数是否相同
- 空函数的参数是否加载到缓存中?
- 使用 lambda 作为构造函数参数是否需要C++ 17?
- 了解'this'或其他参数是否为右值
- const-ref传递的模板化参数是否经过优化,以便在足够小时按值传递
- shared_ptr构造函数参数是否应按值传递
- 如何检查模板参数是否为给定值?
- 使用聚合初始化模拟默认函数参数是否存在任何陷阱?
- 在对象序列化期间添加额外参数是否有更好的方法?
- 通过 ssh 发送参数.是否有非阻塞输入函数?
- 如何检查运算符 != 模板参数是否存在 C++ 17?
- 常量引用函数参数:是否可以禁止临时对象?
- 如何检查模板参数是否为 std::variant?
- 是否可以确定函数的参数是否已签名或无符号,以实现可能性超载函数
- 移动 l 值参考参数是否是一种不好的做法?
- 显式指定通用 lambda 的 operator() 模板参数是否合法?