10

我有以下代码:

template <typename TC>
class C
{
    struct S
    {
        template <typename TS>
        void fun() const
        {}
    };

    void f(const S& s)
    {
        s.fun<int>();
    }
};

// Dummy main function
int main()
{
    return 0;
}

当同时使用 gcc 9.2 和 clang (9.0) 构建它时,由于template调用fun. 铿锵声显示:

error: use 'template' keyword to treat 'fun' as a dependent template name
        s.fun<int>();
          ^
          template 

我不明白为什么编译器认为fun是上下文中的依赖名称f,因为f它本身不是模板。如果我改为C普通类而不是模板,错误就会消失;但是,我不明白为什么首先应该出现错误,因为既不S也不f依赖TC.

奇怪的是,MSVC 19.22 编译得很好。


笔记

在投票结束之前,我必须在哪里以及为什么要放置“模板”和“类型名”关键字?请考虑这是一种特殊情况,即使S确实是一个依赖名称,f如果不是因为它们是当前实例化的成员,在上下文中它也不会依赖。

4

2 回答 2

11

考虑

template<typename T>
struct C
{
    struct S
    {
        int a = 99;
    };

    void f(S s, int i)
    {
        s.a<0>(i);
    }
};

template<>
struct C<long>::S
{
    template<int>
    void a(int)
    {}
};

int main()
{
    C<int>{}.f({}, 0); // #1
    C<long>{}.f({}, 0); // #2
}

s.a<0>(i)被解析为包含两个比较操作<和的表达式>,这对#1 很好,但对#2 失败。

如果将其更改为,s.template a<0>(i)则 #2 正常,而 #1 失败。因此,这里的template关键字永远不会是多余的。

MSVC能够s.a<0>(i)在同一程序中以两种方式解释表达式。但是根据标准这是不正确的;每个表达式应该只有一个解析供编译器处理。

于 2020-02-04T18:22:17.367 回答
7

fun可能是也可能不是模板函数(或可能根本不存在),具体取决于class C.

那是因为您可以专门化S(无需专门化C):

template <> struct C<int>::S {};

因为编译器想fun在第一次查看时class C(在替换模板参数之前)知道是否是模板,template所以是必需的。

于 2020-02-04T18:16:26.630 回答