如果参数具有成员变量,则专用化函数
Specialize function if argument has member variable
我有一个模板化的错误报告函数,因为它可以报告许多不同邮件类的错误:
template <typename MSG>
void reportErr(const MSG& msg)
{
std::cout << "ERROR: " << msg.error << std::endl;
}
但是,某些类型的消息具有可以报告的详细错误或其他专门的错误报告,例如
template<>
void reportErr(const SpecificMsg& msg)
{
std::cout << "ERROR: " << msg.error;
std::cout << ", details: " << msg.details << std::endl;
}
由于有许多类型,例如SpecificMsg
,我宁愿不为每种类型创建单独的模板专用化。是否可以为具有.details
成员变量的任何类型的类型创建泛型专用化/部分专用化?
如果可能的话,我想要一种方法来做这件事(所以如果它有.details
,一个专业化,如果它有.other_info
,另一个,等等(。
编辑:这是明确询问函数。我见过执行类似操作以专门化模板类的代码,但我从未遇到过对非成员函数执行我想要的操作的代码。我怀疑将用于类的方法转换为适用于函数并不难,但我一直无法弄清楚如何做到这一点。
编辑 2:我的 gcc 版本 (4.6.3( 似乎不支持完整的 C++11 标准,因此"重复"问题中提到的void_t
选项对我不起作用。我的编译器抱怨"在'类型'之前有预期的嵌套名称说明符"等,甚至不让我定义void_t。因此,我从我的问题中删除了 C++11 标签。
我会为此使用SFINAE。首先,让我们定义两个函数,它们为消息返回错误字符串:
namespace detail
{
// for messages with "details" member:
template<typename MsgType>
std::string makeMsgString(const MsgType& msg, decltype(MsgType::details)*)
{
return "Error: " + msg.error + ", details: " + msg.details;
}
// for messages without "details" member:
template<typename MsgType>
std::string makeMsgString(const MsgType& msg, ...)
{
return "Error: " + msg.error + ", no details";
}
}
现在,这些函数可以像这样使用:
struct NonSpecificMsg { std::string error; };
struct SpecificMsg { std::string error, details; };
template<typename MsgType>
void reportErr(const MsgType& msg)
{
std::cout << detail::makeMsgString(msg, nullptr) << "n";
}
int main()
{
reportErr(NonSpecificMsg { "some error" }); // 1
reportErr(SpecificMsg { "some other error", "some details" }); // 2
return 0;
}
这里发生了什么?
调用 1(:NonSpecificMsg
没有details
成员,因此不存在第一个重载。由于MsgType::details
不存在,因此decltype(MsgType::details)*
不是有效的类型。SFINAE 会导致忽略此定义,而不是在编译期间引发错误。 只有重载 2(,它不访问details
成员。
调用 2(:SpecificMsg
有details
,因此编译器会考虑这两个重载。但是,可变参数函数重载(第二个(的优先级始终低于任何其他匹配重载,因此选择第一个。
编辑:这是一个C++11解决方案。不幸的是,decltype
是在GCC 4.8中引入的。
编辑2:事实证明,decltype
可以与GCC 4.6一起使用(它是在4.3版本中引入的(。版本 4.8.1 更改了其语义,但在 OP 的情况下,以前的版本将起作用 - 请参阅 GCC 的C++状态页面
可能的话,我想要一种方法来做这件事(所以如果它有 .details,则一个专业化,如果它有 .other_info,则另一个专业化,等等(。
如果我得到了您的期望,您可以将choice
-trick 与decltype
结合使用,如以下示例所示:
#include <iostream>
template<int N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
struct Foo { int error; };
struct Bar { int error; int details; };
struct Quux { int error; char other_info; };
template<typename MSG>
auto reportErr(choice<2>, const MSG& msg) -> decltype(msg.details, void()) {
std::cout << "ERROR: " << msg.error;
std::cout << ", details: " << msg.details << std::endl;
}
template<typename MSG>
auto reportErr(choice<1>, const MSG& msg) -> decltype(msg.other_info, void()) {
std::cout << "ERROR: " << msg.error;
std::cout << ", other_info: " << msg.other_info << std::endl;
}
template <typename MSG>
void reportErr(choice<0>, const MSG& msg) {
std::cout << "ERROR: " << msg.error << std::endl;
}
template <typename MSG>
void reportErr(const MSG &msg) {
reportErr(choice<100>{}, msg);
}
int main() {
reportErr(Foo{0});
reportErr(Bar{0, 42});
reportErr(Quux{0, 'c'});
}
查看它在wandbox
上运行(实际上使用 GCC 4.5.4,您提到的版本不可用(。它利用重载分辨率根据消息的类型选取函数的工作版本,并丢弃介于两者之间的所有内容。您可以添加更多专业化(让我们这样称呼它们,即使它们毕竟不是正确的专业化(,并通过根据需要调整choice
参数来根据您的偏好对它们进行排序(其值越高,专业化的优先级越高(。
choice
技巧与基于 SFINAE 的解决方案中的sizeof
相结合,也可以完成类似的事情,类似于我上面显示的。
特别是,这里有一个工作示例:
#include <iostream>
template<int N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
struct Foo { int error; };
struct Bar { int error; int details; };
struct Quux { int error; char other_info; };
template<typename MSG, std::size_t = sizeof(MSG::details)>
void reportErr(choice<2>, const MSG& msg) {
std::cout << "ERROR: " << msg.error;
std::cout << ", details: " << msg.details << std::endl;
}
template<typename MSG, std::size_t = sizeof(MSG::other_info)>
void reportErr(choice<1>, const MSG& msg) {
std::cout << "ERROR: " << msg.error;
std::cout << ", other_info: " << msg.other_info << std::endl;
}
template <typename MSG>
void reportErr(choice<0>, const MSG& msg) {
std::cout << "ERROR: " << msg.error << std::endl;
}
template <typename MSG>
void reportErr(const MSG &msg) {
reportErr(choice<100>{}, msg);
}
int main() {
reportErr(Foo{0});
reportErr(Bar{0, 42});
reportErr(Quux{0, 'c'});
}
看到它启动并运行在wandbox
.优点是此解决方案不会受到您在前一个解决方案中收到的烦人警告的影响。
我用比你要求的(GCC 4.5.4(更旧的编译器测试了它,所以我非常有信心它们也适用于GCC 4.6.x。
注意:这是在 OP 指定其 gcc/c++ 版本之前编写的 C++17 答案。我让它在那里希望能帮助别人。
您可以标记消息类型并在编译时测试这些标记:
#include <iostream>
#include <type_traits>
#include <string>
struct HasErrorMember { std::string error = "error"; };
struct HasDetailsMember { std::string details = "details"; };
template<class MSG>
void reportErr(const MSG& msg)
{
if constexpr (std::is_base_of_v<HasErrorMember, MSG>) std::cout << "ERROR: " << msg.error;
if constexpr (std::is_base_of_v<HasDetailsMember, MSG>) std::cout << ", details: " << msg.details;
std::cout << "n";
}
struct MsgSimple : HasErrorMember
{};
struct MsgDetails : HasErrorMember, HasDetailsMember
{};
int main()
{
MsgSimple ms;
MsgDetails md;
std::cout << "error only:n";
reportErr(ms);
std::cout << "error + details:n";
reportErr(md);
}
根据您的需求,这些标签可以嵌入成员本身,也可以是空的,将确保成员><标签一致性的责任交给开发人员。>
现场演示
只有 C++03,特征比 C++11 更冗长(如std::is_detected
(,您可以执行以下操作:
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)
template <typename U>
class traitsName
{
private:
template<typename T, T> struct helper;
template<typename T>
static char check(helper<signature, funcName>*);
template<typename T> static int check(...);
public:
static
const bool value = sizeof(check<U>(0)) == sizeof(char);
}
然后
// Would be in std in C++11
template <bool, typename T = void> struct enable_if
{
typedef T type;
};
template <typename T> struct enable_if<false, T>
{
};
然后
DEFINE_HAS_SIGNATURE(has_details, &T::details, std::string (T::*));
template <typename MSG>
typename enable_if<!has_details<MSG>>::type
reportErr(const MSG& msg)
{
std::cout << "ERROR: " << msg.error << std::endl;
}
template <typename MSG>
typename enable_if<has_details<MSG>>::type
void reportErr(const MSG& msg)
{
std::cout << "ERROR: " << msg.error;
std::cout << ", details: " << msg.details << std::endl;
}
演示
- 字符串化递归的"std::vector<std::vector<...>>"而不使用部分模板函数专用化
- 具有常量引用参数的函数模板专用化
- 在 C++20 中是否不再允许在 std 中对程序定义类型的函数模板进行专用化?
- C++模板专用化 - 无法匹配函数定义
- 具有专用化的模板类中的可靠条件复制和移动构造函数
- 如何使专用函数模板成为某个类的朋友?
- 如何按类型为元组元素创建专用函数
- 无法访问专用函数
- 使用指向部分专用函数成员的指针自动填充向量
- 64 位 G++ 4.6.3 不会在专用函数模板中将长整型视为长整型,即使它们的大小相同。这是一个错误吗?
- 操作员过载的专用函数
- C++调用模板专用函数
- 模板类中专用函数的声明
- 使用专用函数对异构集合的 STL 算法
- C++专用函数-不在此范围内错误
- 为什么我不能专用函数模板?
- 用于专用函数参数的模板
- 代码重复和模板专用化(当专用函数具有不同的返回类型时)
- 具有专用函数的Pull-Apart函数类型
- 专用函数模板