在编写线程安全
std::stack
包装器时,我为 push
做了以下两个重载:
void push(const value_type& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(value);
_cv.notify_one();
}
void push(value_type&& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(std::move(value));
_cv.notify_one();
}
它们几乎相同,只是一个采用 const 左值引用,一个采用右值引用。通常我会使用完美转发来处理这个问题(现在使用光荣的 C++20 缩写模板声明):
void push(auto&& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(std::forward<decltype(value)>(value));
_cv.notify_one();
}
这里的问题是它接受任何类型,而它应该只接受
value_type
及其引用。
有一些标准方法可以解决这个问题吗?到目前为止,我想出了两种方法。使用
std::enable_if
以某种方式检查模板类型是否为 value_type
或对其的引用,或者使用概念。
你可以断言它:
template<typename T>
void push(T&& value)
{
static_assert(is_same_v<remove_reference_t<T>, value_type>);
// ...
}
您也可以使用
is_convertible
代替 is_same
,这样效果会更自然。
我在多个版本中重复
@Ayxan Haqverdili
的例子。另请注意,因为您提到您想要一个标准方法来解决这个问题,所以C++20
概念现在是标准方法。除非您编写的代码需要与旧语言版本兼容,否则应该使用它。主要原因是它清楚、简洁地记录了声明站点的功能要求。这甚至比static_assert
更有说服力,并且允许重载而不是直接失败。 std::enable_if
只是设置要求的旧方法。
template <class T>
requires std::convertible_to<T, value_type>
void push(T &&value);
这让您可以使用方便的参数
auto
。
template <class T, class U>
concept ForwardType = std::convertible_to<T, U>
void push(ForwardType<value_type> auto &&value);
std::enable_if
这应该与 C++11 兼容。
template <class T>
std::enable_if_t<std::is_convertible_v<T>> push(T &&value);