调用模板函数是否需要加template关键字

 

验证代码

#include <iostream>

template<typename T>
class MyClass {
public:
    template<typename U>
    void MyMethod()
	{
	    std::cout << 3.14 << std::endl;
    }
    template<typename U>
    void MyMethod2(U value)
	{
	    std::cout << value << std::endl;
    }
};

template <typename T>
void func(MyClass<T> &k)
{
    k.MyMethod<double>();
    k.MyMethod2(3.14);
}

int main()
{
    MyClass<int> myObject;
    myObject.MyMethod();
    myObject.MyMethod2(3.14);
    func(myObject);
}

问题描述

/home/username/Downloads/test_unique/main.cpp: In function ‘void func(MyClass<T>&)’:
/home/username/Downloads/test_unique/main.cpp:21:16: error: expected primary-expression before ‘double’
   21 |     k.MyMethod<double>()
      |                ^~~~~~
/home/username/Downloads/test_unique/main.cpp:21:16: error: expected ‘;’ before ‘double’
   21 |     k.MyMethod<double>()
      |                ^~~~~~
      |                ;
/home/username/Downloads/test_unique/main.cpp: In function ‘int main()’:
/home/username/Downloads/test_unique/main.cpp:28:23: error: no matching function for call to ‘MyClass<int>::MyMethod()’
   28 |     myObject.MyMethod();
      |                       ^
/home/username/Downloads/test_unique/main.cpp:7:10: note: candidate: ‘template<class U> void MyClass<T>::MyMethod() [with U = U; T = int]’
    7 |     void MyMethod()
      |          ^~~~~~~~
/home/username/Downloads/test_unique/main.cpp:7:10: note:   template argument deduction/substitution failed:
/home/username/Downloads/test_unique/main.cpp:28:23: note:   couldn’t deduce template parameter ‘U’
   28 |     myObject.MyMethod();

问题分析

问题1,28行

很简单,因为 MyMethod 没有参数,没办法推断出 U 的类型,所以编译器无法确定调用哪个 MyMethod 函数。

问题2,21行

C++ 标准的 14.2 中规定:

When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

即:

当类的模板成员名称出现在 . 或 -> 在后缀表达式中,或在限定标识符中的嵌套名称说明符之后,并且后缀表达式或限定标识符显式依赖于模板参数(14.6.2),成员模板名称必须是以template关键字为前缀。否则,该名称被假定为非模板名。

所以,k.MyMethod<double>() 应该写成 k.template MyMethod<double>()

这是为了避免歧义,因为 < 可能是小于号,也可能是模板参数列表的开始。而 MyMethod2 因为非显式依赖于模板参数,可以不需要 <>,所以不需要加 template 关键字。

解决方案

  1. 28行,调用 MyMethod 时,需要传入一个类型参数,否则编译器无法推断出 U 的类型。
  2. 21行,调用 MyMethod 时,需要加上 template 关键字。
#include <iostream>

template<typename T>
class MyClass {
public:
    template<typename U>
    void MyMethod()
	{
	    std::cout << 3.14 << std::endl;
    }
    template<typename U>
    void MyMethod2(U value)
	{
	    std::cout << value << std::endl;
    }
};

template <typename T>
void func(MyClass<T> &k)
{
    # 加上template关键字
    k.template MyMethod<double>();
    k.MyMethod2(3.14);
}

int main()
{
    MyClass<int> myObject;
    # 传入一个类型参数, 任意类型都可以,因为不会用到
    myObject.MyMethod<char>();
    myObject.MyMethod2(3.14);
    func(myObject);
}

参考资料

  1. c++模板:调用模板成员函数需不需要加template关键字?
  2. When do we need a .template construct