【发布时间】:2017-10-02 16:59:36
【问题描述】:
我正在尝试编写一个符合 RAII 标准的资源包装器,但在如何形成模板参数的语义方面陷入困境。
例如,我可以编写一个函数来删除我的资源:
void int_cleaner(int val) {
std::cout << "Value of " << val << " has been cleaned up." << std::endl;
}
或者我可以把它写成 Functor:
struct int_deleter {
void operator()(int val) const {
std::cout << "Value of " << val << " has been cleaned up." << std::endl;
}
};
但这就是我卡住的地方:如果我想将它传递给我的资源包装器,我必须更改模板参数的定义方式。
如果我这样写resource:
template<typename T, typename Deleter>
class resource {
};
这适用于仿函数,但不适用于函数本身。
int main() {
resource<int, int_deleter> res; //Compiles fine
//resource<int, int_cleaner> res2; //Does Not Compile
return 0;
}
相反,如果我这样写模板参数:
template<typename T>
using deleter_t = void(*)(T);
template<typename T, deleter_t<T> Deleter>
class resource {
};
int main() {
//resource<int, int_deleter> res; //Does Not Compile
resource<int, int_cleaner> res2; //Compiles fine
return 0;
}
现在,我可以编写两个版本的代码,但我不想这样做有两个原因:
- 我只是复制
resource的定义,如果我需要对其中一个进行更改,我也需要对另一个进行更改。 - 接受函数指针的版本不会接受像
void cleaner(T const&)这样的版本,因为它不会绑定到void(*)(T)。所以我还需要再制作两个或三个版本,以便我可以处理T、T&、T const&和T&&。
我怎样才能以最小化代码重复的方式编写资源包装器,特别是考虑到函数版本和函数指针版本之间的删除机制会有所不同?
//example:
template<typename T>
using deleter_t = void(*)(T);
template<typename T, deleter_t<T> Deleter>
class resource {
~resource() {Deleter(val);}
};
template<typename T, typename Deleter>
class resource {
~resource() {Deleter{}(val);}//Note the subtle syntax change
};
【问题讨论】:
-
std::unique_ptr等。有同样的问题。它们支持按值传递删除器对象,以便您可以传递适当的函数指针。不知道这是否是最好的解决方案。 -
尝试
resource<int, int(&)(int)>使用int_cleaner作为删除器。 -
一种解决方案是创建一个
template <auto FUNCTION> struct wrapper,并将其作为模板参数传递给资源:resource<int, wrapper<int_cleaner>>。我认为你必须做出一些妥协,因为int_cleaner是函数,但int_deleter是一种类型(因此它们不能同时映射到同一个模板参数)。
标签: c++ templates template-meta-programming