【发布时间】:2017-02-10 01:29:20
【问题描述】:
TL;DR;
以下代码的编译以两个未解析的外部结束。
问题
有没有办法在基类中有一个未定义的模板化成员函数,并让该函数在派生类中部分特化,这样部分特化将被限制在定义它的派生类中?
解释
如您所见,serial_port 和 liquid_crystal 都派生自 stream 基类。 stream 类将提供一个统一的接口来将文本发送到不同的外围设备。从stream 派生的每个类都必须实现print(char) 函数,该函数将处理与外围设备的低级通信。除此之外,还有一个未定义的、模板化的 print 版本,可以专门用于用户可能想要打印的任何自定义类型。
stream 类具有operator << 的模板定义,用于将数据写入流。这个操作员会调用stream::print 来处理实际的打印。如您所见,print(const char*) 和 print(fill) 已经定义,因为我希望它们出现在来自stream 的每个派生类中。
现在是引入错误的部分
我想与之通信的外围设备有一些基本命令(LCD:将光标移动到 x,y 坐标,串行端口:将波特率设置为 x)它们之间不可互换,这意味着 LCD 不知道如何更改波特率和串口没有可以移动到特定坐标的光标。我想通过operator << 传递命令,就像我对fill 所做的那样。每个命令都将是一个新结构,其中包含命令所需的数据,并且将有一个专门的 print 版本来处理每个命令。
这在理论上可行,但在编译过程中失败,因为print 的专用版本是在派生类中定义的,而operator << 是在stream 类中定义的。当我将命令传递给流时,链接器会在 stream 中查找 print 的专门定义,当然它会失败,因为那些根本不存在。
我该如何克服这个错误? 我正在使用 Visual Studio 15 Preview 4,并且没有更改任何编译器标志。
源代码
#include <iostream>
class stream
{
public:
struct fill
{
int n;
char ch;
};
stream()
{}
virtual ~stream()
{}
template <typename T>
stream& operator << (T t)
{
this->print(t);
return *this;
}
protected:
virtual void print(char) = 0;
template <typename T>
void print(T);
};
template <>
void stream::print<const char*>(const char* str)
{
while (*str != '\0')
this->print(*(str++));
}
template <>
void stream::print<stream::fill>(stream::fill f)
{
while (f.n > 0)
{
this->print(f.ch);
f.n--;
}
}
class serial_port : public stream
{
public:
struct set_baudrate
{
int baud;
};
using stream::stream;
private:
void print(char c) override
{
// TODO: print to the actual serial port
std::cout << c;
}
template <typename T>
void print(T t)
{
stream::print<T>(t);
}
};
template <>
void serial_port::print<serial_port::set_baudrate>(serial_port::set_baudrate)
{
this->print("set_baudrate");
}
class liquid_crystal : public stream
{
public:
struct move
{
int x;
int y;
};
using stream::stream;
private:
void print(char c) override
{
// TODO: print to a character LCD
std::cout << c;
}
template <typename T>
void print(T t)
{
stream::print<T>(t);
}
};
template <>
void liquid_crystal::print<liquid_crystal::move>(liquid_crystal::move)
{
this->print("move");
}
int main()
{
liquid_crystal lcd;
lcd << liquid_crystal::move{ 1, 2 };
serial_port serial;
serial << serial_port::set_baudrate{ 9600 };
}
编辑
查看compiler output,问题更加明显,链接器正在寻找void stream::print<liquid_crystal::move>(liquid_crystal::move) 和void stream::print<serial_port::set_baudrate>(serial_port::set_baudrate),但函数签名应该是void liquid_crystal::print<liquid_crystal::move>(liquid_crystal::move) 和void serial_port::print<serial_port::set_baudrate>(serial_port::set_baudrate)。
【问题讨论】:
-
您使用什么编译器和标志?不是these。
-
我会立即编辑帖子
-
@LogicStuff 我更新了我的问题和源代码。 Here 是编译器输出,现在它产生与 VS 中相同的错误。
-
嗯... GCC 明确指出问题出在
operator<<(),这使得缩小范围更容易:In function `stream& stream::operator<< <liquid_crystal::move>(liquid_crystal::move)': main.cpp:(.text._ZN6streamlsIN14liquid_crystal4moveEEERS_T_[_ZN6streamlsIN14liquid_crystal4moveEEERS_T_]+0x1f): undefined reference to `void stream::print<liquid_crystal::move>(liquid_crystal::move)' -
出于好奇,您是否有理由不能制作一些可以传递给
operator<<的操纵器函数,类似于std::endl()?
标签: c++ templates inheritance