effective c++ 43-处理模板化基类的名称

程序员小x大约 3 分钟C++C++effective c++读书笔记

effective c++ 43-处理模板化基类的名称

该节主要分析了一个写模板时常常会遇到的一个编译错误。

分析

这里有一个模板基类,有派生类继承了模板基类,并调用了基类中的方法,但是编译器却会报找不该方法,这是怎么回事?

#include <string>
#include <iostream>
class CompanyA
{
public:
	void sendCleartext(const std::string& msg) 
    {
        std::cout << "A sendCleartext" << std::endl;
    }
	void sendEncrypted(const std::string& msg) 
    {
        std::cout << "A sendEncrypted" << std::endl;
    }
};


class CompanyB
{
public:
	void sendCleartext(const std::string& msg) 
    {
        std::cout << "B sendCleartext" << std::endl;
    }
	void sendEncrypted(const std::string& msg) 
    {
        std::cout << "B sendEncrypted" << std::endl;
    }
};


class MsgInfo {
public:
    MsgInfo(std::string msg):msg_(msg){}
private:
	std::string msg_{};
};


template<typename Company>
class MsgSender
{
public:

	void sendClear(const MsgInfo& info)
	{
		std::string msg;
		Company c;
		c.sendCleartext(msg);
	}

	void sendSecret(const MsgInfo& info)
	{
	}
};


template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
	void sendClearMsg(const MsgInfo& info)
	{
        sendClear(info);
	}
};

int main()
{
    MsgInfo info("test");
    LoggingMsgSender<CompanyB> loggingMsgSender;
    loggingMsgSender.sendClearMsg(info);
}

编译输出如下:

/home/work/cpp_proj/test2/main.cpp: In member function ‘void LoggingMsgSender<Company>::sendClearMsg(const MsgInfo&)’:
/home/work/cpp_proj/test2/main.cpp:63:9: error: there are no arguments to ‘sendClear’ that depend on a template parameter, so a declaration of ‘sendClear’ must be available [-fpermissive]
         sendClear(info);
         ^~~~~~~~~
/home/work/cpp_proj/test2/main.cpp:63:9: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
/home/work/cpp_proj/test2/main.cpp: In instantiation of ‘void LoggingMsgSender<Company>::sendClearMsg(const MsgInfo&) [with Company = CompanyB]’:
/home/work/cpp_proj/test2/main.cpp:71:39:   required from here
/home/work/cpp_proj/test2/main.cpp:63:18: error: ‘sendClear’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
         sendClear(info);
         ~~~~~~~~~^~~~~~
/home/work/cpp_proj/test2/main.cpp:63:18: note: declarations in dependent base ‘MsgSender<CompanyB>’ are not found by unqualified lookup
/home/work/cpp_proj/test2/main.cpp:63:18: note: use ‘this->sendClear’ instead

从编译的输出也可以看出,原因是编译器觉得sendClear含义不明确,编译器也给出了解决办法,使用this->sendClear,这里我们要思考的是,为什么编译器会找不到sendClear函数呢? 不是就定义在模板基类中吗?

实际上原因就是基类是一个模板类, 而模板类是可以被特化的,例如这里又有了一个CompanyZ,而MsgSender对于CompanyZ的特化版本可能就没有sendClear方法,因此编译器才会说sendClear含义不明确。

class CompanyZ
{
public:
	void sendEncrypted(const std::string& msg) {}
};


template<>
class MsgSender<CompanyZ>
{
public:
	void sendSecret(const MsgInfo& info)
	{
	}
};

好了,知道了原因之后,那么就可以给出解决办法了,其实上面编译器也已经给出来一种办法。

第一种就是在派生类中调用模板基类中的方法时加上this->

template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
	void sendClearMsg(const MsgInfo& info)
	{
        this->sendClear(info);
	}
};

have a tryopen in new window

第二种就是在派生类中调用模板基类中的方法时加上使用using

template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
using MsgSender<Company>::sendClear;

public:
	void sendClearMsg(const MsgInfo& info)
	{
		sendClear(info);
	}
};

have a tryopen in new window

第三种就是在派生类中明确指出使用MsgSender<Company>::sendClear(info)

template<typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
	void sendClearMsg(const MsgInfo& info)
	{
		MsgSender<Company>::sendClear(info);
	}
};

have a tryopen in new window

最后提一句,如果子类不是一个模板类,编译时也是不会有问题的,因为很明确。

class LoggingMsgSender : public MsgSender<CompanyB>
{
public:
	void sendClearMsg(const MsgInfo& info)
	{
        sendClear(info);
	}
};

总结

  • 在派生类模板中如果需要调用模板基类的方法,需要使用this->,或者明确指出名称。
Loading...