在 child class 中定义可变函数特化
Defining variadic function specialization in child class
我正在尝试允许 child class 定义可变函数特化。例如:
#include <iostream>
#include <vector>
#include <memory>
class BaseClass
{
public:
BaseClass() {};
virtual ~BaseClass() {};
template<typename GenericData>
void ReceiveData(GenericData &inData)
{
throw std::runtime_error("Undefined");
}
};
class ReceiveInt : public BaseClass
{
void ReceiveData(int & inData)
{
std::cout << "I know what to do!";
}
};
int main(int argc, char* argv[])
{
std::vector<std::shared_ptr<BaseClass>> classHolder;
classHolder.push_back(std::make_shared<ReceiveInt>());
int test = 1;
classHolder.front()->ReceiveData(test);
return 0;
}
但不幸的是,这不起作用,因为调用了 BaseClass ReceiveData 函数。这甚至可能吗?
编辑 1
正如人们所指出的,我的符号非常错误。看来我今天学到的比我想象的要多。
Is this even possible?
我不这么认为。
动态调度仅适用于 virtual
成员函数。
成员函数模板可能不是virtual
。
如果您可以使用常规成员函数,即不是成员函数模板,那么您可以制作它们 virtual
。那行得通。
我认为您可能混淆了您的术语。 BaseClass::ReceiveData
是一个模板化方法,采用 模板参数 GenericData
。可变参数函数采用运行时确定的多个 参数 。
在 ReceiveInt
中,您没有对任何内容进行专门化,因为 ReceiveInt::ReceiveData
不是模板化方法。事实上,即使它是模板化的,也不可能在你的例子中调用。指向 BaseClass
的指针如何知道如何在它指向的派生 class 中调用模板特化?
你可以BaseClass::ReceiveData
virtual
。这允许您在基 class 中覆盖它,并且仍然使用指向 BaseClass
的指针调用它。不幸的是,模板是一种编译时语言特性,而动态分派是一种运行时特性——在这种情况下,你不能同时拥有这两种特性。
参考资料
Why do we need virtual functions in C++?
您必须先转换为派生类型,使用基 class pointer/reference 是不可能的,因为基 class 只会知道它自己的实现。这甚至不是您可以对派生类型使用递归依赖的情况,因为派生类型在实例化基础时尚未定义。
如果您确实强制转换为派生类型,那么它将能够按照您的需要解析派生成员。
你在这里混淆了一些概念。
首先,这里没有可变模板作为下面的 ReceiveData
函数:
template<typename GenericData>
void ReceiveData(GenericData &inData)
{
throw std::runtime_error("Undefined");
}
是模板成员函数。
那么,如果你想在派生的class中覆盖一个方法,正确的方法是使用virtual函数,大概是一个pure基 class 中的 virtual 函数和派生 class 中带有 override 说明符的 virtual 函数].
但是,virtual
函数将您限制为一组固定类型,因为没有模板虚函数。不过,您可以尝试使用 CRTP:
template<typename T>
class Base {
public:
void receiveData(const T&) {}
};
class ReceiveInt : public Base<int> {};
模拟了一种静态多态性。以下:
ReceiveInt{}.receiveData(int{});
receiveData
来自基础 class,实例化为 int
。
您的代码中没有其他人已经解释过的可变参数模板。
但是您可以使用这样一个事实,即模板化 class 方法是在第一次调用时实例化的。但是这里没有虚拟覆盖。
在此示例中,您可以在 Base 和 Derived classes 中定义方法模板的不同实现,但您已明确告诉编译器使用哪一个。
在没有显式转换的情况下,无法通过 Base class 指针使用 Derived class 方法:
#include <iostream>
#include <memory>
using namespace std;
class Base
{
public:
Base() {};
virtual ~Base() {};
template<typename T>
void ReceiveData(T)
{
throw std::runtime_error("Undefined");
}
};
class Derived : public Base
{
public:
template<typename... Args >
void ReceiveData(Args... args)
{
(void)std::initializer_list<int>{(std::cout << args << std::endl, 0)...};
}
};
int main()
{
Base b;
// b.ReceiveData(1); //-> this calls base class method
Derived d;
d.ReceiveData(1); // this calls the method in the derived class
d.ReceiveData(2, "hello"); // this calls the method in the derived class
Base* b2 = new Derived();
// b2->ReceiveData(3); // this will instantiate and call the base class method
// thus raising an exception
// because no virtual overriding of templated methods
((Derived*)b2)->ReceiveData("world",1); // this will instantiate and call the derived class
// method, then because of explicit casting the
// compiler knows which class to target
return 0;
}
我正在尝试允许 child class 定义可变函数特化。例如:
#include <iostream>
#include <vector>
#include <memory>
class BaseClass
{
public:
BaseClass() {};
virtual ~BaseClass() {};
template<typename GenericData>
void ReceiveData(GenericData &inData)
{
throw std::runtime_error("Undefined");
}
};
class ReceiveInt : public BaseClass
{
void ReceiveData(int & inData)
{
std::cout << "I know what to do!";
}
};
int main(int argc, char* argv[])
{
std::vector<std::shared_ptr<BaseClass>> classHolder;
classHolder.push_back(std::make_shared<ReceiveInt>());
int test = 1;
classHolder.front()->ReceiveData(test);
return 0;
}
但不幸的是,这不起作用,因为调用了 BaseClass ReceiveData 函数。这甚至可能吗?
编辑 1 正如人们所指出的,我的符号非常错误。看来我今天学到的比我想象的要多。
Is this even possible?
我不这么认为。
动态调度仅适用于 virtual
成员函数。
成员函数模板可能不是virtual
。
如果您可以使用常规成员函数,即不是成员函数模板,那么您可以制作它们 virtual
。那行得通。
我认为您可能混淆了您的术语。 BaseClass::ReceiveData
是一个模板化方法,采用 模板参数 GenericData
。可变参数函数采用运行时确定的多个 参数 。
在 ReceiveInt
中,您没有对任何内容进行专门化,因为 ReceiveInt::ReceiveData
不是模板化方法。事实上,即使它是模板化的,也不可能在你的例子中调用。指向 BaseClass
的指针如何知道如何在它指向的派生 class 中调用模板特化?
你可以BaseClass::ReceiveData
virtual
。这允许您在基 class 中覆盖它,并且仍然使用指向 BaseClass
的指针调用它。不幸的是,模板是一种编译时语言特性,而动态分派是一种运行时特性——在这种情况下,你不能同时拥有这两种特性。
参考资料
Why do we need virtual functions in C++?
您必须先转换为派生类型,使用基 class pointer/reference 是不可能的,因为基 class 只会知道它自己的实现。这甚至不是您可以对派生类型使用递归依赖的情况,因为派生类型在实例化基础时尚未定义。
如果您确实强制转换为派生类型,那么它将能够按照您的需要解析派生成员。
你在这里混淆了一些概念。
首先,这里没有可变模板作为下面的 ReceiveData
函数:
template<typename GenericData>
void ReceiveData(GenericData &inData)
{
throw std::runtime_error("Undefined");
}
是模板成员函数。
那么,如果你想在派生的class中覆盖一个方法,正确的方法是使用virtual函数,大概是一个pure基 class 中的 virtual 函数和派生 class 中带有 override 说明符的 virtual 函数].
但是,virtual
函数将您限制为一组固定类型,因为没有模板虚函数。不过,您可以尝试使用 CRTP:
template<typename T>
class Base {
public:
void receiveData(const T&) {}
};
class ReceiveInt : public Base<int> {};
模拟了一种静态多态性。以下:
ReceiveInt{}.receiveData(int{});
receiveData
来自基础 class,实例化为 int
。
您的代码中没有其他人已经解释过的可变参数模板。 但是您可以使用这样一个事实,即模板化 class 方法是在第一次调用时实例化的。但是这里没有虚拟覆盖。 在此示例中,您可以在 Base 和 Derived classes 中定义方法模板的不同实现,但您已明确告诉编译器使用哪一个。 在没有显式转换的情况下,无法通过 Base class 指针使用 Derived class 方法:
#include <iostream>
#include <memory>
using namespace std;
class Base
{
public:
Base() {};
virtual ~Base() {};
template<typename T>
void ReceiveData(T)
{
throw std::runtime_error("Undefined");
}
};
class Derived : public Base
{
public:
template<typename... Args >
void ReceiveData(Args... args)
{
(void)std::initializer_list<int>{(std::cout << args << std::endl, 0)...};
}
};
int main()
{
Base b;
// b.ReceiveData(1); //-> this calls base class method
Derived d;
d.ReceiveData(1); // this calls the method in the derived class
d.ReceiveData(2, "hello"); // this calls the method in the derived class
Base* b2 = new Derived();
// b2->ReceiveData(3); // this will instantiate and call the base class method
// thus raising an exception
// because no virtual overriding of templated methods
((Derived*)b2)->ReceiveData("world",1); // this will instantiate and call the derived class
// method, then because of explicit casting the
// compiler knows which class to target
return 0;
}