如果成员函数存在则执行






4.94/5 (7投票s)
使用 C++ 模板? 希望你的模板算法能够灵活地与那些未完全支持所需接口的类一起工作? 想用 C++ 实现更多函数式编程? 现在就来实现吧!
引言
我在使用 string
时遇到了这个问题。 我的类支持哈希码生成,但 std::string
不支持,而模板算法却能从中受益。 怎么办? 解决方案是 SFINAE。
背景
SFINAE - 替换失败不是错误。
这个规则应用于函数模板的重载解析:当为模板参数推导出的类型进行替换失败时,该特化将从重载集中移除,而不是导致编译错误。
此特性用于模板元编程。
Using the Code
头文件本身:(我将其命名为“FuncUtils.h”)
#ifndef FuncUtilsH
#define FuncUtilsH
#include <utility>
#define EXEC_MEMBER_PROC_IF_PRESENT(ProcName) namespace ProcName {\
void ExecIfPresent(...) throw() {}\
\
template <class C, typename... TArgs>\
void ExecIfPresent(C& obj, TArgs&... args) {\
obj.ProcName(std::forward<TArgs>(args)...);\
}\
};
// If NOT exists - returns the 'DefaultValue'
// 'DefaultValue' SHOULD be the same type as a decltype(*.FuncName())
// Works with static/const/virtual funcs
#define EXEC_MEMBER_FUNC_IF_PRESENT(FuncName, DefaultValue) namespace FuncName {\
template <typename TReturnType = decltype(DefaultValue)>\
auto ExecIfPresent(...) -> TReturnType {\
return std::move(DefaultValue);\
}\
\
template <class C, typename... TArgs>\
auto ExecIfPresent(C& obj, TArgs&... args)\
-> decltype(obj.FuncName(std::forward<TArgs>(args)...))\
{/* do NOT use 'const C& obj' NOR 'C::FuncName()!'*/\
return std::move(obj.FuncName(std::forward<TArgs>(args)...));\
}\
};
#endif // FuncUtilsH
要使用它,只需“实例化”一个宏,提供函数名称和默认值(可以是常量、字面量、引用、类成员等)
EXEC_MEMBER_FUNC_IF_PRESENT(getHashIfKnown, size_t())
调用
const auto hash = getHashIfKnown::ExecIfPresent(str);
关注点
这个模块只是 使用 C++11 特性的库 的一小部分,我目前正在开发它,我决定将其设为public
属性。
历史
更新 1
// <Include the above code>
// While my MS VS Community 2013 Update 5 ok with 'size_t()' as a default val.,
// the Ideone wouldn't compile it, so changed to '0U' literal
EXEC_MEMBER_FUNC_IF_PRESENT(getHashIfKnown, 0U);
#include<cassert>
#include<cstring>
#include<string>
#include<iostream>
int main() {
std::string str = "test str";
auto hash = getHashIfKnown::ExecIfPresent(str);
assert(!hash);
std::cout << "str: " << hash << std::endl;
struct MyStr {
MyStr(const char* str) throw() {
if(str) {
strcpy(buf, str);
len = strlen(buf);
}
}
size_t getHashIfKnown() const throw() {
size_t hash = 0U;
for(size_t idx = 0U; idx < len; ++idx) {
hash += buf[idx] * (idx + 1U);
}
return hash;
}
size_t len = 0U;
char buf[256U];
} myStr = "we don't need no water!";
hash = getHashIfKnown::ExecIfPresent(myStr);
assert(hash);
std::cout << "myStr: " << hash << std::endl;
}
输出
str: 0
myStr: 24355
更新 2
GitHub 仓库链接 已添加。
更新 3
可下载的归档文件已替换为新的修复、更新和重新设计的版本 1.01。
请注意,实际所做的更改并未在文章的文本中描述,甚至未提及(以原始版本呈现),你可以在 GitHub 仓库中查看它们