确定可调用对象是否具有模板参数

时间:2017-10-15 21:26:19

标签: c++ templates c++14

是否可以确定可调用对象的第一个参数(函数,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

2 个答案:

答案 0 :(得分:1)

不,我不相信这是可能的。

函数模板f1非常难以使用,因为在类型或表达式中几乎所有可能的f1用法都必须立即将其转换为特定类型,只选择一个特殊的模板,否则立即导致编译器错误。没有办法只传递模板名称,因此可以在SFINAE上下文中使用,因为模板模板参数仅用于类模板和别名模板,从不用于函数模板。

您实际上会遇到与重载的非模板函数集名称完全相同的问题。

但有一件事更接近于帮助:将所需的函数模板或重载函数包装在通用lambda中。现在它至少可以用作模板参数。并且问题被简化为找出f3

如果给定的类型以某种方式已知或假设为闭包类型,则检测它是否是通用lambda的类型很简单:表达式&F::operator()有效对于非泛型lambda而对于泛型lambda无效。此外,您可以检测是否可以或不能使用特定的参数类型列表调用任何可调用类型,和/或是否可以通过类型推导将特定可调用类的operator()专用于得到一个特定的参数类型列表

但就我认为我们可以接受它而言。实际上有无数个可能的参数/参数类型列表。如果你能以某种方式限制你关心检查有限列表的可能性,你可以尝试所有。 (或者,如果你可以选择一组不依赖于任意标识符的可能的可能性,你可以尝试尽可能多的编译器的最大实例化数量。)但这并没有真正解决问题。如果多个参数类型作为第一个参数有效,则它仍然可以是非模板函数,其第一个参数类型可以隐式地从多个类型转换。或者即使您只能确定一个第一个参数类型是有效的,或者只有一个函数参数列表是有效的,您仍然可以拥有一个函数模板,其第一个参数是依赖类型,它只能根据一个特定类型求值。通过其他方式推导出可能的模板参数。

答案 1 :(得分:0)

理论上编译器可以知道它。

但不是。 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解决其中一些问题并将功能包装在测试模板中。再次,它将是丑陋的,通常不起作用。