C ++-为什么这里需要'template'关键字?


9

我有以下代码:

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。lang声显示:

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,如果不是因为它们是当前实例的成员,也就不会从属。


评论不作进一步讨论;此对话已转移至聊天
巴尔加夫Rao

Answers:


10

考虑

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,则OK,并且#1失败。因此,template关键字在这里永远不会多余。

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


我仍然不完全明白这一点。您的示例显示,在这种情况下,您将使用C或的另一种,但是无法实例化两者。template这里的关键字仍然不需要IMHO,因为选择哪个关键字S取决于C您实例化的对象。没有template关键字,您将能够实例化这两个实例,并且f的行为对于每个实例都会有所不同。
马丁

2
@Martin的要点是,每个标记在源文件中应只具有一个语法角色。例如,令牌<不能在一个模板实例中充当比较运算符,而在另一实例中充当开放角括号。这是为了确保编译器可以将模板解析为AST(带有模板类型的占位符)。
ecatmur

这就说得通了。谢谢!
马丁

7

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

这是因为您可以专门化S(不专门化C):

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

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


1
头脑...吹...
bolov

接下来,是能否S通过访问这些新的定义f。如果他们做不到,则没有这个限制是没有道理的,因为f无论如何都看不到它们。
马丁

@Martin同时使用GCC,Clang和MSVC f都可以找到它。
HolyBlackCat

@bolov是的,您可以专门研究许多不同的东西
HolyBlackCat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.