Reputation: 4925
When writing a thread-safe std::stack
wrapper I made the following two overloads for 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();
}
They are nearly the same except that one takes a const lvalue reference and one takes an rvalue reference. Normally I would use perfect forwarding to deal with this (now using the glorious C++20 abbreviated template declaration):
void push(auto&& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(std::forward<decltype(value)>(value));
_cv.notify_one();
}
The problem here is that this accepts any type, while it should accept only value_type
and references to it.
Is there some standard way to solve this? So far I came up with two approaches. Either use a std::enable_if
to somehow check if the template type is value_type
or a reference to it, or use a concept.
Upvotes: 4
Views: 947
Reputation: 2715
I repeat @Ayxan Haqverdili
's example in multiple versions. Also note, because you mention that you want a standard way to solve this, that C++20
concepts are the standard way now. Unless you are writing code that requires compatibility with old language versions, that should be used. The primary reason is that it clearly and concisely documents the requirements of the function at the declaration site. This is even more eloquent than static_assert
, and allows overloading instead of direct failure. std::enable_if
is simply the old method of instating requirement.
template <class T>
requires std::convertible_to<T, value_type>
void push(T &&value);
This lets you use the convenient parameter auto
.
template <class T, class U>
concept ForwardType = std::convertible_to<T, U>
void push(ForwardType<value_type> auto &&value);
std::enable_if
This should be compatible with C++11.
template <class T>
std::enable_if_t<std::is_convertible_v<T>> push(T &&value);
Upvotes: 3
Reputation: 29975
You can assert it:
template<typename T>
void push(T&& value)
{
static_assert(is_same_v<remove_reference_t<T>, value_type>);
// ...
}
You could alternatively use is_convertible
instead of is_same
so it works more naturally.
Upvotes: 4