Xir*_*ema 6 c++ templates template-meta-programming
我正在尝试编写符合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
};
做
template<typename T, typename Deleter>
class resource {
};
然后写
template<auto k>
using constant_t = std::integral_constant<std::decay_t<decltype(k)>, k>;
template<auto k>
constexpr constant_t<k> constant{};
现在您的主要内容如下所示:
int main() {
  resource<int, int_deleter> res; //Compiles fine
  resource<int, constant_t<int_cleaner>> res2; //Also compiles fine
  return 0;
}
我们已经完成了。
这是c++17。
在c++14 中,您必须替换constant_t<foo>with,std::integral_constant<std::decay_t<decltype(foo)>, foo>因为它缺少auto模板参数。
在c++11 integral_constant中不能使用函数指针并让你调用它们。您必须编写派生类型:
namespace notstd {
  template<class T, T t>
  struct integral_constant:std::integral_constant<T, t> {
    constexpr operator T()const{ return this->get(); }
  }
}
和替换std::integral_constant用notstd::integral_constant启用该功能。(隐式转换为函数指针足以允许在整数常量上使用调用运算符)。
在c++03 中,你会想要一个新的编译器。
C++17 中的另一种方法是使用所有值而不是所有类型。
resource foo(7, int_deleter{});
resource foo2(7, int_cleaner);
并教导资源为删除者保留价值。这导致int_cleaner获取存储并在资源中具有价值。
resource foo(7, int_deleter{});
resource foo2(7, constant<int_cleaner>);
回到最初的计划,我们int_cleaner通过将它提升到类型系统来创建一个无状态指针。
通过使用 EBO,resource可以零开销存储无状态删除器。
请注意,您的resource容貌一个很多像unique_ptr<T, Deleter>,在那里Deleter::pointer是一个薄包裹std::optional<T>(对null)。
template<class T>
struct nullable_opt:std::optional<T> {
  using std::optional<T>::optional;
  nullable_opt( nullable_opt const& ) = default;
  nullable_opt( nullable_opt && ) = default;
  nullable_opt& operator=( nullable_opt const& ) = default;
  nullable_opt& operator=( nullable_opt && ) = default;
  nullable_opt() = default;
  nullable_opt(std::nullptr_t) {}
};
或诸如此类。
| 归档时间: | 
 | 
| 查看次数: | 561 次 | 
| 最近记录: |