我想实现一个类型擦除类AnyFunction
,它能够存储任何带有模板化调用运算符(返回void
(的实体。例如:
struct Printer {
template<typename... Args>
void operator()(Args&&... args) {
std::cout << ... << std::forward<Args>(args);
}
};
Printer typed_printer
AnyFunction printer(typed_printer);
printer(1,2.,"3");
应打印CCD_ 3。我了解如何为具有呼叫操作员已知签名的实体执行此操作。例如,这是有效的:
struct AddFunc {
void operator()(int a, int b) {
std::cout << a << "+" << b << "=" << a + b << std::endl;
}
};
struct PrintSmthFunc {
void operator()(int a, double b, const char *c) {
std::cout << a << b << c << std::endl;
}
};
int main() {
AddFunc f1;
AnyFunction addFunc(f1);
addFunc(10, 20);
PrintSmthFunc f2;
AnyFunction printSmthFunc(f2);
printSmthFunc(1, 2., "3");
}
(以下是实现:https://godbolt.org/z/r4zq6f81G)。但我不明白在模板化运算符的一般情况下如何做到这一点。。。我认为完全打字抹掉它是不可能的。
你不能完全满足你的要求。
您可以编写类型AnyPrinter
,但如果不交付C++编译器并将要存储在其中的C++代码作为输入并动态编译DLL,就无法编写AnyFunction
。
DLL的存在应该使问题变得清楚。假设您的AnyFunction
是在AnyFunction.h
中定义的。
我们在DLLPrinterCode.dll
中编译Printer
。然后,我们在Foo.dll
中编译另一种类型,称之为Foo
。
我们将AnyPrinter
传递给AnyFunction
,并从PrinterCode.dll
返回它。
然后,您的可执行文件加载两个dll,将AnyFunction
传递给Foo.dll
,后者用私有类型Foo
调用它。
知道如何打印Foo
的代码住在哪里?
可执行文件无法访问Printer
的源代码。CCD_ 20没有访问CCD_ 21的权限。并且CCD_ 22不能访问CCD_。
C++二进制文件不包含与您编写的代码等效的数据。它们包含该代码到标准中指定的抽象机器的投影,然后投影到机器代码。
其他语言可以实现这类功能,因为它们的运行时表示要么包括您编写的代码的所有内容,要么其通用代码不如模板强大,实际上只是自动运行时类型转换包装器。
现在,如果您想要类似Java泛型的东西,C++可以做到这一点。
您可以编写Printable
,但它看起来不像C++模板代码。
struct Printer {
void operator()(std::initializer_list<PrintableRef> args) {
for(auto arg:args)
std::cout << arg;
}
};
我可以让工作。诀窍是我们在PrintableRef
中进行工作。
CCD_ 26类型擦除到"0";我可以打印";。这里有一个快速的:
struct PrintableRef {
void(*print)(void*, std::ostream&) = nullptr;
void* pdata = nullptr;
template<class T> requires (!std::is_same_v<std::decay_t<T>, PrintableRef>)
PrintableRef(T&& t):
print([](void* pvoid, std::ostream& os) {
os << *static_cast<decltype(std::addressof(t))>(pvoid);
}),
pdata(std::addressof(t))
{}
PrintableRef(PrintableRef const&)=default;
~PrintableRef()=default;
PrintableRef()=delete;
friend
std::ostream& operator<<(std::ostream& os, PrintableRef self) {
self.print(self.pvoid, os);
return os;
}
};
但这还没有达到CCD_ 27的程度。
必须有人知道类型和您希望在代码中的某个时刻打印它的事实。
不幸的是,这在C++中是不可能的。一般来说,类型擦除通过运行时多态性工作,这需要虚拟函数。
使AnyFunction
签名不可知的唯一方法是将其operator()
作为模板。但模板不可能是虚拟的。因此,你说不可能";完全";键入擦除它。