【发布时间】:2020-01-29 20:41:25
【问题描述】:
在Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true? 问题之后,我正在尝试编写一种与 C++ 98 兼容的方式来打印任何函数指针。
为此,我使用了一个伪造的 C++“可变参数”模板,即,将所有函数定义写入最多 n 个参数。但是,我的假可变参数仅适用于具有 0 个参数的函数指针,如下例所示:https://godbolt.org/z/x4TVHS
#include<iostream>
template<typename Return>
std::ostream& operator <<(std::ostream& os, Return(*pointer)() ) {
return os << (void*) pointer;
}
template<typename Return, typename T0>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( const T0& t0 ) ) {
return os << (void*) pointer;
}
template<typename Return, typename T0, typename T1>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( const T0& t0, const T1& t1 ) ) {
return os << (void*) pointer;
}
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main() {
std::cout << "1. " << fun_void_void << std::endl;
std::cout << "2. " << fun_void_double << std::endl;
std::cout << "3. " << fun_double_double << std::endl;
}
// Prints:
// 1. funptr 0x100401080
// 2. funptr 1
// 3. funptr 1
如果我使用 C++11 真正的可变参数模板编写等效版本,那么一切正常:https://godbolt.org/z/s6wdgp
#include<iostream>
template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( Args... ) ) {
return os << (void*) pointer;
}
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main() {
std::cout << "1. " << fun_void_void << std::endl;
std::cout << "2. " << fun_void_double << std::endl;
std::cout << "3. " << fun_double_double << std::endl;
}
// Prints:
// 1. funptr 0x100401080
// 2. funptr 0x100401087
// 3. funptr 0x100401093
分析代码后,我注意到与 then 的唯一区别是 C++11 示例中的类型不是 const 引用。然后,我从 C++98 中删除了常量和引用,它开始工作:https://godbolt.org/z/ZrF66b
#include<iostream>
template<typename Return>
std::ostream& operator <<(std::ostream& os, Return(*pointer)() ) {
return os << (void*) pointer;
}
template<typename Return, typename T0>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( T0 ) ) {
return os << (void*) pointer;
}
template<typename Return, typename T0, typename T1>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( T0, T1 ) ) {
return os << (void*) pointer;
}
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main() {
std::cout << "1. " << fun_void_void << std::endl;
std::cout << "2. " << fun_void_double << std::endl;
std::cout << "3. " << fun_double_double << std::endl;
}
// Prints:
// 1. funptr 0x100401080
// 2. funptr 0x100401087
// 3. funptr 0x100401093
为什么函数指针模板定义在 const 和/或引用时不匹配?
【问题讨论】:
-
因为它们是不同的类型?不知道你为什么对
double(*)(double)不匹配double(*)(const double&)感到困惑 -
您能否解释一下为什么这种行为不是您所期望的?似乎很明显有例如没有类型
Return和T0,这样Return(*)(const T0&)就是double(*)(double)。 -
我不会想到这种行为,因为当我构建模板函数时,描述其参数的最通用方式是
const Type& arg(在新版本的 C++ 中,我可以使用完美转发和 @ 987654335@)。虽然在这里使它工作的唯一方法是使用Type arg。只需使用此签名,它就可以接受任何其他函数指针类型为const Type arg(与参数传递相反)。 -
@user 也许你对模板的工作方式有误解。模板参数将替换为某种类型(显式给出或以某种方式推导),然后调用该函数,就好像它是具有替换参数的非模板函数一样。如果您要为函数调用
operator<<(std::cout, fun_double_double)显式提供模板参数,您会为T0提供什么类型,您认为这会导致函数调用格式正确? -
顺便说一句。
Type arg作为函数参数比const Type& arg或Type&& arg更通用。您可以显式地为Type提供任何类型,无论是否引用,因此使用Type arg获取任何参数类型,但使用const Type& arg,您只能将const引用作为参数类型,即使您尝试明确指定类型。只是Type arg的引用类型永远不会自动推导出来,而且它更经常将参数作为泛型代码中的引用,特别是如果您不知道所涉及的类型是否可以复制/移动。
标签: c++ c++11 pointers templates c++98