为什么编译器不能从函数参数中推断出我的模板值?
Why can't the compiler deduce my template value from the function argument?
以下将无法编译:
enum E {A,B,C};
template<E m>
void foo(E m) {}
int main() {
foo(A);
return 0;
}
我得到的错误是:
- 'E m' 的声明:void foo(E m) {}:阴影模板参数 'E m'
- 错误:没有匹配函数来调用 'foo(E)':
foo(A);
- 候选人是:template void foo(E) : void foo(E m) {}
- 模板参数 deduction/substitution 失败:无法推断模板参数 'm' : foo(A);
我不明白这里到底出了什么问题。为什么编译器不能从函数参数推导出模板参数?
我需要做什么才能完成这项工作?
如果你想要一个 运行 时间参数,使用:
void foo(E m) {}
它的值 m
类型为 E
。 (注意:不需要 template<E m>
)
如果你想要一个编译时参数,使用:
template<E m>
void foo() {}
并调用:
foo<A>();
或者如果您希望 foo
适用于所有枚举类型:
template<typename E>
void foo(E m) {}
并可能用
检查枚举
static_assert(std::is_enum<E>::value, "E is not an enumeration");
在函数体内。 (如果需要,您还可以使用 SFINAE 从重载集中删除 foo
,询问您是否需要帮助)
更新:解释您的原始代码及其错误之处:
template<E m> void foo(E m) {}
// ^^^ (1) ^^^ (2)
(1) 是 E
类型的编译时参数 m
,(2) 是 运行 时参数 also 称为 m
,类型也是 E
。由于它具有相同的名称,因此第二个参数隐藏了第一个参数。在您的函数中使用名称 m
将只能访问第二个参数,您无法访问第一个参数。现在考虑:
template<E m1> void foo(E m2) {}
现在您可以访问不同名称下的参数,即 m1
和 m2
。如果你这样调用函数:
foo<A>(B);
那么m1
就是A
,m2
就是B
。而且两者仍然属于相同的固定类型 E
。它们是独立的参数,运行-time 参数的值不会用于编译时参数。
根据您需要的类型或参数(编译时或 运行 时),您只需省略不需要的参数,最终得到上述实现之一。
我不是 100% 清楚你真正的问题在哪里,这对初学者来说并不罕见,因为公认很难描述你并不真正理解的问题,但请确保你首先了解两者之间的区别编译时间和 运行 时间参数以及如何使用它们。
我猜你想写:
enum E {A,B,C};
template<typename T>
void foo(T m) {}
int main() {
foo(A);
return 0;
}
我终于明白为什么不能编译了。尽管在我的特定情况下,函数参数是编译时常量(枚举成员),但通常函数参数是 运行 时间相关的。模板值必须是编译时常量,因此函数参数值不允许用于模板值推导。如果执行此操作的机制是隐藏名称,那么我认为这是偶然的。
结论是,虽然模板函数类型可以从函数参数类型推导出来,但模板值是不可推导的。
以下将无法编译:
enum E {A,B,C};
template<E m>
void foo(E m) {}
int main() {
foo(A);
return 0;
}
我得到的错误是:
- 'E m' 的声明:void foo(E m) {}:阴影模板参数 'E m'
- 错误:没有匹配函数来调用 'foo(E)': foo(A);
- 候选人是:template void foo(E) : void foo(E m) {}
- 模板参数 deduction/substitution 失败:无法推断模板参数 'm' : foo(A);
我不明白这里到底出了什么问题。为什么编译器不能从函数参数推导出模板参数?
我需要做什么才能完成这项工作?
如果你想要一个 运行 时间参数,使用:
void foo(E m) {}
它的值 m
类型为 E
。 (注意:不需要 template<E m>
)
如果你想要一个编译时参数,使用:
template<E m>
void foo() {}
并调用:
foo<A>();
或者如果您希望 foo
适用于所有枚举类型:
template<typename E>
void foo(E m) {}
并可能用
检查枚举static_assert(std::is_enum<E>::value, "E is not an enumeration");
在函数体内。 (如果需要,您还可以使用 SFINAE 从重载集中删除 foo
,询问您是否需要帮助)
更新:解释您的原始代码及其错误之处:
template<E m> void foo(E m) {}
// ^^^ (1) ^^^ (2)
(1) 是 E
类型的编译时参数 m
,(2) 是 运行 时参数 also 称为 m
,类型也是 E
。由于它具有相同的名称,因此第二个参数隐藏了第一个参数。在您的函数中使用名称 m
将只能访问第二个参数,您无法访问第一个参数。现在考虑:
template<E m1> void foo(E m2) {}
现在您可以访问不同名称下的参数,即 m1
和 m2
。如果你这样调用函数:
foo<A>(B);
那么m1
就是A
,m2
就是B
。而且两者仍然属于相同的固定类型 E
。它们是独立的参数,运行-time 参数的值不会用于编译时参数。
根据您需要的类型或参数(编译时或 运行 时),您只需省略不需要的参数,最终得到上述实现之一。
我不是 100% 清楚你真正的问题在哪里,这对初学者来说并不罕见,因为公认很难描述你并不真正理解的问题,但请确保你首先了解两者之间的区别编译时间和 运行 时间参数以及如何使用它们。
我猜你想写:
enum E {A,B,C};
template<typename T>
void foo(T m) {}
int main() {
foo(A);
return 0;
}
我终于明白为什么不能编译了。尽管在我的特定情况下,函数参数是编译时常量(枚举成员),但通常函数参数是 运行 时间相关的。模板值必须是编译时常量,因此函数参数值不允许用于模板值推导。如果执行此操作的机制是隐藏名称,那么我认为这是偶然的。 结论是,虽然模板函数类型可以从函数参数类型推导出来,但模板值是不可推导的。