Идеальная переадресация с определенным типом

При написании поточно-ориентированной оболочки 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();
}

Они почти одинаковы, за исключением того, что берется константная ссылка на l-значение, а другая — на r-значение. Обычно я бы использовал идеальную пересылку, чтобы справиться с этим (теперь использую великолепное сокращенное объявление шаблона С++ 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 или ссылкой на него, либо используйте концепцию.


person janekb04    schedule 27.07.2020    source источник
comment
Вы также можете использовать static_assert. Или вы можете просто не заморачиваться, потому что push(...) сама не скомпилируется, если тип значения неверен.   -  person jtbandes    schedule 27.07.2020
comment
Оба упомянутых вами подхода не допускают конверсий, например. для типа значения double push(0) не будет работать. У меня самого нет лучшего решения, я использую концептуальный подход.   -  person Dani    schedule 27.07.2020
comment
Вместо этого используйте std::is_convertible.   -  person IlCapitano    schedule 27.07.2020
comment
Значит, auto&& будет обрабатывать l-значения и r-значения? Я всегда отстаю лет на 5... Возможно, я разберусь с функциями C++20 в 2025 году.   -  person wcochran    schedule 28.07.2020
comment
@wcochran void foo(auto&&) — это синтаксический сахар для template <typename T> void foo(T&&)   -  person NathanOliver    schedule 28.07.2020


Ответы (1)


Вы можете утверждать это:

template<typename T>
void push(T&& value)
{
    static_assert(is_same_v<remove_reference_t<T>, value_type>);
    // ...
}

В качестве альтернативы вы можете использовать is_convertible вместо is_same, чтобы он работал более естественно.

person Ayxan Haqverdili    schedule 27.07.2020
comment
@NathanOliver Я попытался немедленно удалить ответ и отредактировать эту часть, как только заметил это, и вам все же удалось оставить комментарий :-) - person Ayxan Haqverdili; 28.07.2020
comment
Что я могу сказать, я ниндзя ;) Вы должны рассмотреть возможность использования std::is_convertible вместо std::is_same, чтобы вещи, которые можно преобразовать в value_type, также принимались. - person NathanOliver; 28.07.2020