【问题标题】:How to write a template that can deduce a type use a function's argument type?如何编写可以使用函数的参数类型推断类型的模板?
【发布时间】:2019-01-11 06:15:26
【问题描述】:

如何编写一个使用函数作为模板参数的模板,并通过该函数的参数类型自动推断出其他类型名?

void foo(int *p) {}

template<typename T, void (*F)(T*)>
struct bar
{
    bar(T* t)
    {
        F(t);
    }
}

int *p;
bar<int, foo> b(p); // both int and foo are required here

如何编写支持仅使用foo 作为参数的模板

bar<foo> b(p);

【问题讨论】:

  • 就我所见(承认不是很近),你不能。 T 处于不可演绎的上下文中。您可以使F 成为类型化模板参数(与您现在拥有的非类型化参数相反),然后通过模板将推导移至构造函数。我得考虑一会儿。即使在这种情况下,您也需要在 b 的 decl 上使用 decltype。
  • 你需要一个 auto 模板参数,它需要 C++17。
  • @n.m.如果你的意思是template&lt;void (*F)(auto)&gt; struct bar,那么 g++ 给了我error: ‘auto’ parameter not permitted in this context
  • 不,这不是 auto 的工作方式。 coliru.stacked-crooked.com/a/3f589f17e8bfd766 是一种方法。
  • 谢谢@n.m。那行得通,但是如果没有c ++ 17会更好。更喜欢 c++11 > c++14 > c++17

标签: c++ templates


【解决方案1】:

如果您可以将 c++17 与它的自动模板参数一起使用(如 cmets 中所说的 @n.m.),您可以将其用作模板参数,然后将类型 T 与类型特征一起使用。

首先,我们需要标准类型特征和类型特征来获取一元函数(您的 foo)的参数,我们可以这样编写:

#include <type_traits>

// Trait to get the argument type of a unary function
template<typename T>
struct unary_func_arg;

template<typename R, typename T>
struct unary_func_arg< R(*)(T) >
{
    using type = T;
};

如果你将函数指针以外的任何东西放入其中,这将产生错误,因为未声明主特化。

在这之后,我们终于可以把 bar 写成这样了:

template< auto F >
struct bar
{
    // Assert it's a function pointer
    static_assert( std::is_pointer_v<decltype(F)> );
    static_assert( std::is_function_v< std::remove_pointer_t<decltype(F)> > );

    // Get the parameter type
    using T = typename unary_func_arg< decltype(F) >::type;

    bar(T t)
    {
        F(t);
    }
};

我们必须确保 F 是一个函数指针,所以我们静态断言它,然后我们从我们的类型特征中获取类型 T。

现在你可以像这样声明 f 和 b:

int* p;
bar<foo> b(p);

编辑:如果您需要 T 不是指针以便可以编写 T*,您可以创建一个类型特征来删除 1 个指针级别,或者在此处修改类型特征:

// Trait to get the argument type of a unary function
template<typename T>
struct unary_func_arg_pointer;

template<typename R, typename T>
struct unary_func_arg_pointer< R(*)(T*) >
{
    using type = T;
};

现在 T 在这个例子中只是 int

【讨论】:

    【解决方案2】:

    在 C++11 中,类不能推导出传递函数的所有类型。但是一个函数可以。 所以这个函数可以写成:

    template<typename Ret, typename Param>
    Deduced_Types<Ret, Param> deduce_type(Ret (*)(Param))
    {
        return Deduced_Types<Ret, Param>();
    }
    

    此函数使用类型来存储推导的类型(必须在函数之前声明):

    template<typename Ret, typename Param>
    class Deduced_Types
    {
    public:
        typedef Ret Return_type;
        typedef Param Parameter_type;
    };
    

    现在来测试一下;

    int f(bool)
    {
        return 0;
    }
    
    int main(int argc, char* argv[])
    {
        decltype( deduce_type(f) )::Return_type i = 0;
    
        return i;
    }
    

    有效。

    那么现在来吧:

    template< class F >
    class Bar
    {
    public:
        typedef typename F::Return_type Func_Return_type;
        typedef typename F::Parameter_type Fun_Param_type;
    
    };
    

    必须调用:

    Bar< decltype(deduce_type(f)) > b;
    

    (这是你可以使用宏的地方)

    适用于 gcc 4.8.1:https://godbolt.org/z/1cz2pk


    编辑:

    Pre C++17 函数无法传递到模板中。

    所以需要的是你需要将函数指针传递给类。并static_assert验证参数是否正确:

    #include <type_traits>
    
    struct bar
    {
        template<typename F, typename T>
        bar(F f, T t)
        {
            typedef decltype(deduce_type(f)) D;
            static_assert(std::is_same<typename D::Parameter_type, T>::value,"parameter has different type function parameter");
    
            f(t);
        }
    };
    

    现在不是将函数作为模板参数传递,而是将函数指针作为参数传递:

    void foo(int *p) {}
    
    int *p;
    bar b(foo, p); 
    

    这里唯一的问题是类必须存储这个指针以备将来使用。

    【讨论】:

    • 我认为在函数指针本身而不是类型上进行模板化的目的是为放入的每个不同函数生成一个唯一模板,这不会那样做,因为它是在函数类型,否则他可以将声明更改为template&lt;typename F&gt;并将其用作bar&lt;decltype(foo)&gt;,然后从类型F中提取T。
    【解决方案3】:

    在 C++17 中,您可能会这样做

    template <auto> struct bar;
    
    template<typename T, void (*F)(T*)>
    struct bar<F>
    {
        bar(T* t) { F(t); }
    };
    

    有用法:

    int* p = nullptr;
    bar<foo> b(p);
    

    在 C++17 之前,您可能会这样做:

    template <typename T, T> struct bar;
    
    template<typename T, void (*F)(T*)>
    struct bar<void (*)(T*), F>
    {
        bar(T* t) { F(t); }
    };
    

    有用法:

    int* p = nullptr;
    bar<decltype(&foo), &foo> b(p);
    

    MACRO 可用于删除重复,例如:

    #define BAR(Func) bar<decltype(&Func), &Func>
    #define TYPE_AND_VALUE(V) decltype(V), V>
    

    允许:

    BAR(foo) b(p);
    bar<TYPE_AND_VALUE(&foo)> b(p);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-12
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      相关资源
      最近更新 更多