模板别名如何影响模板参数推导?

13

在C++03中,模板参数推导不会在某些上下文中发生。例如:

template <typename T> struct B {};

template <typename T>
struct A
{
    typedef B<T> type;
};

template <typename T>
void f(typename A<T>::type);

int main()
{
    B<int> b;
    f(b);  // ERROR: no match
}

在这里,int并没有被推导为T,因为像A<T>::type这样的嵌套类型是一个无法推导的上下文。
如果我把函数写成这样:
template <typename T> struct B {};

template <typename T>
void f(B<T>);

int main()
{
    B<int> b;
    f(b);
}

一切都很好,因为B<T>一个推断的上下文。

然而,在C++11中,模板别名可以被用来伪装嵌套类型,语法类似于第二个示例。例如:

template <typename T> struct B {};

template <typename T>
struct A
{
    typedef B<T> type;
};

template <typename T>
using C = typename A<T>::type;

template <typename T>
void f(C<T>);

int main()
{
    B<int> b;
    f(b);
}

在这种情况下,模板参数推导是否会起作用?换句话说,模板别名是被推导的上下文还是非推导的上下文?还是它们继承了它们所别名的任何东西的推断/非推断状态?


1
别名只不过是别名。这就像写template <typename T> void f(typename A<T>::type);一样,这不能被推导出来。 - Kerrek SB
可能是C++,模板参数无法推导的重复问题。 - Nawaz
我认为Kerrek SB是正确的。如果提供了引用,我就不需要去搜索它了;-) - Dietmar Kühl
2
@Nawaz:我不认为这是重复的问题:这个问题是关于模板别名的,而你指向的问题中我看不到任何模板别名的提示。 - Dietmar Kühl
1
@Nawaz:在标记为重复之前,请确实阅读问题... - HighCommander4
糟糕,我误读了问题。 - Nawaz
3个回答

11
换句话说,模板别名是被推导的上下文还是非被推导的上下文? 它们与不使用模板别名的等效代码一样可被推导。例如:
template<typename T>
using ref = T&;

template<typename T>
void f(ref<T> r);

现在你可以调用f(x)T将被完美推导。在f的定义时,ref<T>已经被替换为类型T&。而T&是一个被推断的上下文。
在你的情况中,C<T>被替换为typename A<T> :: type,这是一个不被推断的上下文,因此T无法被推导。

2
Johannes的优秀答案中的关键短语是“在f定义时间已经”(强调我的)。很容易忽略这一点,但知道它可以解释一切。 - Dave Abrahams

2

想象一下:

template <typename T> struct Foo { typedef   T type; }
template <> struct Foo<char>     { typedef int type; }

template <typename T> using mytype = typename Foo<T>::type;

template <typename T> void f(mytype<T>);

现在,如果我想要 int n; f(n);,我该如何决定我想要 T = int 还是 T = char?整个问题,并不受模板别名影响,就是你无法向后推导出所有可能定义某些东西的事情。

1

我认为C++标准中相关的引用是14.5.7 [temp.alias]第2段:

当模板ID引用别名模板的特化时,它等同于通过将其模板参数替换为别名模板的类型ID中的模板参数获得的关联类型。[注意:别名模板名称永远不会被推导。—注]

引用后面有一个例子,有效地说明在函数模板中使用别名模板并希望推导模板参数是没有意义的。这显然适用于即使不涉及嵌套类型的情况。


你最后的陈述是不正确的。你所引用的有关推断的语句是说,无法推断别名模板名称。也就是说,template<template<typename T> class C> void f(C<T>); ref<int> r; f(r); 无法推断 C == ref(根据我在我的回答中定义的 ref)。这并不意味着你得出的结论。 - Johannes Schaub - litb
好的,我可以接受这个。不过,昨天我没有找到更好的引用(但我可能被搞混了)。你能指向标准中的一个引用吗? - Dietmar Kühl
1
你引用了自己。根据你所引用的内容,模板ID ref<T> 等同于 T&。因此,非规范性注释指出它不能用于推导 ref。但是,由于这种早期替换,它可用于推导 T。请参见 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#395 和 https://dev59.com/Smw15IYBdhLWcg3wbLLU#6623089 的解决方案。 - Johannes Schaub - litb

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