C++ - 为什么这里需要“模板”关键字?

C++ - Why is the 'template' keyword required here?

提问人:Martin 提问时间:2/5/2020 最后编辑:curiousguyMartin 更新时间:2/5/2020 访问量:2716

问:

我有以下代码:

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) 构建此内容时,由于调用需要关键字,因此出现编译错误。Clang 显示:templatefun

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

我不明白为什么编译器认为是上下文中的依赖名称,因为它本身不是模板。如果我更改为常规类而不是模板,则错误会消失;但是,我不明白为什么首先应该出现错误,因为既不依赖也不依赖.funffCSfTC

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


注意

在投票关闭之前,我必须在哪里以及为什么必须放置“模板”和“typename”关键字? 请注意,这是一种特殊情况,即使确实是依赖名称,如果不是因为它们是当前实例化的成员,则在它的上下文中也不会是依赖名称。Sf

C++ 模板 language-lawyer dependent-name

评论

0赞 Bhargav Rao 2/5/2020
评论不用于扩展讨论;此对话已移至 Chat

答:

8赞 HolyBlackCat 2/5/2020 #1

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

那是因为你可以专精(不专精):SC

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

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

评论

0赞 Martin 2/5/2020
那么,接下来的问题是,这些新的定义是否可以通过访问。如果他们不能,那么有这个限制就没有意义,因为无论如何都看不到他们。Sff
0赞 HolyBlackCat 2/5/2020
@Martin GCC、Clang 和 MSVC 确实找到了它。f
0赞 HolyBlackCat 2/5/2020
@bolov是的,你可以专注于很多不同的东西
14赞 ecatmur 2/5/2020 #2

请考虑

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 来说失败了。<>

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

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

评论

0赞 Martin 2/5/2020
我仍然不完全理解这一点。您的示例表明,在本例中,您要么使用 的专用化,要么使用 另一个,但您永远不能同时实例化两者。恕我直言,这里的关键字仍然是不需要的,因为选择哪个取决于您实例化的内容。如果没有关键字,您将能够同时实例化两者,并且每个实例的 f 行为都会有所不同。CtemplateSCtemplate
5赞 ecatmur 2/5/2020
@Martin关键是,每个标记在源文件中应该只有一个语法角色。例如,令牌不能在一个模板实例化中充当比较运算符,而在另一个实例化中充当左尖括号。这是为了确保编译器可以将模板解析为 AST(模板类型带有占位符)。<
0赞 Martin 2/5/2020
这是有道理的。谢谢!