模板方法的模板模板参数推导失败(尽管明确专门化)
template-template-parameter deducation failed for template method (although explicitly-specialized)
我正在尝试编写一个可通过 C++ 扩展的小型脚本解释器。为此目的,函数处理程序被插入到调度 table 中。
为了简化我的问题,处理程序类型定义如下(在实际代码中,它包含参数列表和 return 类型的参数):
// Handler type
using function_type = void(int);
分派 table 目前是一个简单的无序映射,具有(某种)错位名称作为实现重载的关键:
// Dispatch table
std::unordered_map<std::string, function_type*> fn_handlers;
方法直接添加到此 table,例如就像一个简单的方法,它接受两个 int
类型的参数(operator+ii
是我的管理名称):
fn_handlers["operator+ii"] = my_add_handler;
然而,许多处理程序,尤其是与基础数学相关的处理程序,确实接受各种参数,int
和 double
的所有组合都是有效的,产生 4 个方法和 4 个分派 table条目。因此我决定使用模板来实现这些方法。
举个例子,这可能基本上是这样的(再次简单化):
template<class A, class B>
void my_add_handler(int /* simplified... */)
{
// Something here A and B are needed
}
dispatch table然后这样填充:
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 内完成):
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
模板:
handler_provider<int, int>::add<my_add_handler<int,int>>("test");
但我想忽略的正是这个重复(twict <int,int>
)。
但是,我在最后一部分中总是遇到错误。 handler_provider::add
方法定义如下(上面提到的参数名称修改被省略,因为它不是这里的重点并且按预期工作):
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;
}
};
如上所述,第一个重载是针对我上面描述的确切情况,下面的处理程序安装非模板函数和完全专用的函数。
但是,这给了我一个错误,说明无法从如上所示的调用中推断出内部模板。我不认为我告诉编译器推断出任何东西,我完成了调用中的模板参数的专门化(再次):
handler_provider<int, int>::add<my_add_handler>("operator+");
外部可变参数模板 class... Ts
的参数明确命名为 <int, int>
,内部模板模板的简单参数命名为 my_add_handler
。但是,编译器似乎忽略了这个(?)。这是我得到的输出(gcc 5.4.0 使用 -std=c++14
):
$ 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) 更精确一点:
$ 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.
但是我还是不明白我哪里错了。我错过了什么?
谢谢,
塞巴斯蒂安
为了更好地测试,这里有一个完整的例子:
#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
}
问题如下:根据标准,[temp.arg.template]/1,
[a] template-argument for a template template-parameter shall be the name of a class template or an alias
template, expressed as id-expression.
因此您无法实例化模板
template<template<class...> class F>
static void add(const std::string name) {
handler_provider<Ts...>::add<F<Ts...>>(name);
}
使用函数模板test_handler
。
要解决此问题,您必须将 test_handler
改为模板仿函数,即将其更改为
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
重载调整为
// Overload for templates, such as 'test_handler'
template<template<class...> class F>
static void add(const std::string name) {
fn_handlers[name] = F<Ts...>{};
}
完整代码现在如下所示:
#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
}
这完全符合您的要求,如 coliru.
所示
如果你不想使用 std::function
因为开销(在我的平台上 std::function
使用 32 字节而不是 8 字节的指针)你也可以只写你自己的类型处理程序的擦除结构。
我正在尝试编写一个可通过 C++ 扩展的小型脚本解释器。为此目的,函数处理程序被插入到调度 table 中。 为了简化我的问题,处理程序类型定义如下(在实际代码中,它包含参数列表和 return 类型的参数):
// Handler type
using function_type = void(int);
分派 table 目前是一个简单的无序映射,具有(某种)错位名称作为实现重载的关键:
// Dispatch table
std::unordered_map<std::string, function_type*> fn_handlers;
方法直接添加到此 table,例如就像一个简单的方法,它接受两个 int
类型的参数(operator+ii
是我的管理名称):
fn_handlers["operator+ii"] = my_add_handler;
然而,许多处理程序,尤其是与基础数学相关的处理程序,确实接受各种参数,int
和 double
的所有组合都是有效的,产生 4 个方法和 4 个分派 table条目。因此我决定使用模板来实现这些方法。
举个例子,这可能基本上是这样的(再次简单化):
template<class A, class B>
void my_add_handler(int /* simplified... */)
{
// Something here A and B are needed
}
dispatch table然后这样填充:
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 内完成):
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
模板:
handler_provider<int, int>::add<my_add_handler<int,int>>("test");
但我想忽略的正是这个重复(twict <int,int>
)。
但是,我在最后一部分中总是遇到错误。 handler_provider::add
方法定义如下(上面提到的参数名称修改被省略,因为它不是这里的重点并且按预期工作):
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;
}
};
如上所述,第一个重载是针对我上面描述的确切情况,下面的处理程序安装非模板函数和完全专用的函数。
但是,这给了我一个错误,说明无法从如上所示的调用中推断出内部模板。我不认为我告诉编译器推断出任何东西,我完成了调用中的模板参数的专门化(再次):
handler_provider<int, int>::add<my_add_handler>("operator+");
外部可变参数模板 class... Ts
的参数明确命名为 <int, int>
,内部模板模板的简单参数命名为 my_add_handler
。但是,编译器似乎忽略了这个(?)。这是我得到的输出(gcc 5.4.0 使用 -std=c++14
):
$ 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) 更精确一点:
$ 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.
但是我还是不明白我哪里错了。我错过了什么?
谢谢,
塞巴斯蒂安
为了更好地测试,这里有一个完整的例子:
#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
}
问题如下:根据标准,[temp.arg.template]/1,
[a] template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.
因此您无法实例化模板
template<template<class...> class F>
static void add(const std::string name) {
handler_provider<Ts...>::add<F<Ts...>>(name);
}
使用函数模板test_handler
。
要解决此问题,您必须将 test_handler
改为模板仿函数,即将其更改为
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
重载调整为
// Overload for templates, such as 'test_handler'
template<template<class...> class F>
static void add(const std::string name) {
fn_handlers[name] = F<Ts...>{};
}
完整代码现在如下所示:
#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
}
这完全符合您的要求,如 coliru.
所示如果你不想使用 std::function
因为开销(在我的平台上 std::function
使用 32 字节而不是 8 字节的指针)你也可以只写你自己的类型处理程序的擦除结构。