模板特化和别名模板推断的区别

9

我很难理解在以下情况下扣除是如何工作的:

template<class Category, Category code>
struct AImpl
{ };

template<class Category, Category code>
struct AHelper
{
    using type = AImpl<Category, code>;
};

template<class Category, Category code>
using A = typename AHelper<Category, code>::type;

template<int code>
void doSomething(A<int, code> object)
{
}

以下是测试代码:

A<int, 5> a1;
doSomething(a1); // This does not compile
doSomething<5>(a1); // This compiles

为什么在这种情况下没有推导出a1?
如果您改为以以下方式修改A:
template<class Category, Category code>
struct A
{ };

两种都可以。有人知道为什么吗?

[编辑] 问题链接:混合别名和模板特化

1个回答

8

为什么在这个上下文中a1没有被推导出来?

因为doSomething的模板参数出现在一个无法推断的上下文中。一个别名模板几乎完全代表了它所代替的内容。而你的别名模板定义如下:

template<class Category, Category code>
using A = typename AHelper<Category, code>::type;

为了推导出代码,编译器需要推导::左侧的内容,这是一种无法推导的上下文。如果出现作为作用域解析运算符左侧参数的情况,模板参数推导甚至不会尝试推断任何内容。
这是一种无法推导的上下文并非没有理由。请记住,模板可以被专门化。我可以添加以下内容:
template<Category code>
struct AHelper<int, code>
{
    using type = BImpl<code>; // My own class!
};

编译器需要查看整个程序中的所有代码,并尝试所有类型,以确保没有任何恶意行为发生,以确定真正匹配为typename AHelper<Category, code>::type。这是不可行的。因此,元函数映射只是单向的。您无法要求编译器从目标类型中推导出源类型(或非类型参数)。

谢谢您的反馈,非常有趣。我创建了AHelper以提供更多的灵活性,但如果这样失去了自动扣除,那可能就不值得了。是否有任何解决方法可以使用,也许可以以某种方式修改doSomething? - Saturnu
1
@Saturnu - 你可以定义它接受 AImpl。这样,你就可以让客户端代码更轻松地定义对象,同时函数始终得到所需的内容。虽然不完美,但这就是 C++。 - StoryTeller - Unslander Monica
我已经考虑过这个问题,但是如果有人创建了自己的AHelper,那么这个函数就不再起作用了。你需要为每种AHelper专业化创建一个重载,但这不是我想要的。 - Saturnu
1
@Saturnu - 解决方案是使用命名空间。如果你把你的代码放在 namespace yourLib {} 中,它就不会与其他代码冲突。只要 yourLib 足够独特即可。 - StoryTeller - Unslander Monica
我的代码已经在命名空间中了。我似乎漏掉了什么。 - Saturnu
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接