首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >模板-模板-参数推导失败的模板方法(虽然明确-专门)

模板-模板-参数推导失败的模板方法(虽然明确-专门)
EN

Stack Overflow用户
提问于 2016-10-15 19:28:06
回答 1查看 1.2K关注 0票数 2

我正在尝试编写一个可以通过c++扩展的小型脚本解释器。为此,函数处理程序被插入到调度表中。为了简化我的问题,handlertype定义如下(在实际代码中,它包含参数列表和返回类型的参数):

代码语言:javascript
运行
复制
// Handler type
using function_type = void(int);

目前,调度表是一个简单的无序映射,其名称(某种程度上)是实现重载的关键:

代码语言:javascript
运行
复制
// Dispatch table
std::unordered_map<std::string, function_type*> fn_handlers;

方法直接添加到这个表中,例如,一个简单的方法,它接受int类型的两个参数(在我的例子中,operator+ii是这个参数的固定名称):

代码语言:javascript
运行
复制
fn_handlers["operator+ii"] = my_add_handler;

但是,许多处理程序,特别是那些与基本数学有关的处理程序,确实接受各种参数,intdouble的所有组合都是有效的,产生4个方法和4个分派表条目。因此,我决定使用模板来实现这些方法。举个例子,这可能基本上是这样(再次简化):

代码语言:javascript
运行
复制
template<class A, class B>
void my_add_handler(int /* simplified... */)
{
  // Something here A and B are needed
}

然后,调度表如下所示:

代码语言:javascript
运行
复制
fn_handlers["operator+ii"] = my_add_handler<int,int>;
fn_handlers["operator+di"] = my_add_handler<double,int>;
fn_handlers["operator+id"] = my_add_handler<int,double>;
fn_handlers["operator+dd"] = my_add_handler<double,double>;

还是有很多要输入的,但现在还可以。无论如何,由于模板参数与方法签名(损坏的名称)之间显然有关联,所以我尝试自动编写(参数名称管理将在handler_provider::add中完成):

代码语言:javascript
运行
复制
handler_provider<int, int>::add<my_add_handler>("operator+");
handler_provider<double, int>::add<fn_add_handler>("operator+");
handler_provider<int, double>::add<fn_add_handler>("operator+");
handler_provider<double, double>::add<fn_add_handler>("operator+");

然后,它将在开始时接受参数,并将它们作为第二种类型的模板参数(这样就不必两次键入<int, int>部分)。

为了澄清起见,我知道我会显式地专门化my_add_handler模板,如下所示:

代码语言:javascript
运行
复制
handler_provider<int, int>::add<my_add_handler<int,int>>("test");

但正是这种复制,我想省略( <int,int>)。

然而,我总是在最后一部分中出现错误。handler_provider::add方法的定义如下(上面提到的参数名mangeling被排除在外,因为它不是这里的重点,并且按预期工作):

代码语言:javascript
运行
复制
template<class... Ts>
struct handler_provider
{
  // Overload for templates, such as 'test_handler'
  template<template<class...> class F>
  static void add(const std::string name)
  {
    handler_provider<Ts...>::add<F<Ts...>>(name);
  }

  // Overload for non-template (or already specialized) handlers (aka. function pointers)
  template<function_type F>
  static void add(const std::string name)
  {
    fn_handlers[name] = F;
  }
};

如前所述,第一个重载应该是针对我前面描述的情况,下面的处理程序安装非模板函数和那些完全专门化的函数。

但是,这给了我一个错误,告诉我不能从上面所示的调用中推断出内部模板。我认为我根本没有让编译器推断任何事情,我完成了调用中的模板参数的专门化(再次):

代码语言:javascript
运行
复制
handler_provider<int, int>::add<my_add_handler>("operator+");

外部变量模板class... Ts的参数显式命名为<int, int>,内部模板的简单参数命名为my_add_handler。然而,编译器似乎忽略了这个(?)。这是我得到的输出(gcc 5.4.0使用-std=c++14):

代码语言:javascript
运行
复制
$ g++ -std=c++14 sci_e1.cpp -o sci
sci_e1.cpp: In function ‘int main()’:
sci_e1.cpp:45:55: error: no matching function for call to ‘handler_provider<int, int>::add(const char [5])’
  handler_provider<int, int>::add<my_add_handler>("operator+");
                                                             ^
sci_e1.cpp:17:15: note: candidate: template<template<class ...> class typedef F F> static void handler_provider<Ts>::add(std::__cxx11::string) [with F = F; Ts = {int, int}]
  static void add(const std::string name)
              ^
sci_e1.cpp:17:15: note:   template argument deduction/substitution failed:
sci_e1.cpp:24:15: note: candidate: template<void (* F)(int)> static void handler_provider<Ts>::add(std::__cxx11::string) [with void (* F)(int) = F; Ts = {int, int}]
  static void add(const std::string name)
              ^
sci_e1.cpp:24:15: note:   template argument deduction/substitution failed:
sci_e1.cpp:45:55: error: could not convert template argument ‘my_add_handler’ to ‘void (*)(int)’
  handler_provider<int, int>::add<my_add_handler>("operator+");
                                                             ^

我得到了第二个错误,这是完全可以的,不应该是一个问题,因为这个过载应该被踢出过载解析的模板类型。第一个错误是那个让我发疯的错误。

Clang (3.9.0)稍微精确一点:

代码语言:javascript
运行
复制
$ clang++ -std=c++14 sci_e1.cpp -o sci
sci_e1.cpp:45:3: error: no matching function for call to 'add'
  handler_provider<int, int>::add<my_add_handler>("test");
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sci_e1.cpp:17:15: note: candidate template ignored: invalid explicitly-specified argument for
      template parameter 'F'
  static void add(const std::string name)
              ^
sci_e1.cpp:24:15: note: candidate template ignored: invalid explicitly-specified argument for
      template parameter 'F'
  static void add(const std::string name)
              ^
1 error generated.

但我仍然不明白我在这里错了什么。我遗漏了什么?

谢谢,

塞巴斯蒂安

为了进行更好的测试,下面是一个完整的示例:

代码语言:javascript
运行
复制
#include <unordered_map>
#include <string>
#include <iostream>

// Handler type
using function_type = void(int);

// Dispatch table
std::unordered_map<std::string, function_type*> fn_handlers;

// Handler provider (to install new handlers)
template<class... Ts>
struct handler_provider
{
  // Overload for templates, such as 'test_handler'
  template<template<class...> class F>
  static void add(const std::string name)
  {
    handler_provider<Ts...>::add<F<Ts...>>(name);
  }

  // Overload for non-template (or already specialized) handlers (aka. function pointers)
  template<function_type F>
  static void add(const std::string name)
  {
    fn_handlers[name] = F;
  }
};


template<class A, class B>
void test_handler(int v)
{
  // Something here A and B are needed
}

void other_handler(int v)
{
  // A handler without specialization
}

int main()
{
  // Install handlers
  handler_provider<int, int>::add<test_handler>("testii");
  handler_provider<double, int>::add<test_handler>("testdi");
  handler_provider<bool, bool, int>::add<other_handler>("otherbbi");

  // Dispatch
  fn_handlers["testii"](5); // Sould call test_handler<int, int>
  fn_handlers["testdi"](5); // Should call test_handler<double, int>

  fn_handlers["otherbbi"](5); // Should call other_handler
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-10-16 21:57:01

问题如下:根据标准tem.arg.Template/1,

模板模板参数的模板参数应该是类模板或别名模板的名称,表示为id表达式。

因此,无法实例化模板

代码语言:javascript
运行
复制
template<template<class...> class F>
static void add(const std::string name) {
    handler_provider<Ts...>::add<F<Ts...>>(name);
}

使用函数模板test_handler

要解决这个问题,您必须将test_handler改为模板函子,即将其更改为

代码语言:javascript
运行
复制
template<class A, class B>
struct test_handler {
    void operator()(int v) {
        // Something here A and B are needed
        std::cout << __PRETTY_FUNCTION__ << " called with v = " << v << std::endl;
    }
};

不幸的是,现在这不再是void(*)(int)类型了,所以不能将它插入到unordered_map中。因此,您必须将映射中的元素更改为std::function<function_type>,并将模板函子的add重载调整为

代码语言:javascript
运行
复制
// Overload for templates, such as 'test_handler'
template<template<class...> class F>
static void add(const std::string name) {
    fn_handlers[name] = F<Ts...>{};
}

完整的代码现在看起来如下所示:

代码语言:javascript
运行
复制
#include <iostream>
#include <functional>
#include <string>
#include <unordered_map>

// Handler typ
using function_type = void(int);

// Dispatch table
std::unordered_map<std::string, std::function<function_type>> fn_handlers;

// Handler provider (to install new handlers)
template<class... Ts>
struct handler_provider {
    // Overload for templates, such as 'test_handler'
    template<template<class...> class F>
    static void add(const std::string name) {
        fn_handlers[name] = F<Ts...>{};
    }

    // Overload for non-template (or already specialized) handlers (aka. function pointers)
    template<function_type F>
    static void add(const std::string name) {
        fn_handlers[name] = F;
    }
};

template<class A, class B>
struct test_handler {
    void operator()(int v) {
        // Something here A and B are needed
        std::cout << __PRETTY_FUNCTION__ << " called with v = " << v << std::endl;
    }
};

void other_handler(int v) {
    // A handler without specialization
    std::cout << __PRETTY_FUNCTION__ << " called with v = " << v << std::endl;
}

int main() {
    // Install handlers
    handler_provider<int, int>::add<test_handler>("testii");
    handler_provider<double, int>::add<test_handler>("testdi");
    handler_provider<bool, bool, int>::add<other_handler>("otherbbi");

    // Dispatch
    fn_handlers["testii"](5); // Sould call test_handler<int, int>
    fn_handlers["testdi"](5); // Should call test_handler<double, int>

    fn_handlers["otherbbi"](5); // Should call other_handler
}

这正是您想要的,在这个柯尔鲁中可以看到。

如果您不想使用std::function,因为开销(在我的平台上,std::function为指针使用32字节而不是8字节),您也可以为处理程序编写自己的类型擦除结构。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40063336

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档