C++ Разрешение перегрузки с универсальным эталонным шаблоном функции, которое нельзя изменить
Предположим, где-то в моем коде есть функция foo
с универсальным ссылочным параметром, который я не могу изменить:
template<typename T>
auto foo(T&& t) { std::cout<<"general version"<<std::endl; }
Теперь хочу перегрузить foo
для данного класса A
и убедитесь, что для любого классификатора и ссылочного типа A
перегрузка называется. Для этого я могу грубо обеспечить перегрузку для всех возможных квалификаций (игнорировать volatile
теперь):
auto foo(A & a) { std::cout<<"A&"<<std::endl; }
auto foo(A const& a) { std::cout<<"A const&"<<std::endl; }
auto foo(A && a) { std::cout<<"A &&"<<std::endl; }
auto foo(A const&& a) { std::cout<<"A const&&"<<std::endl; }
Демо Это, однако, очень плохо масштабируется для большего количества параметров.
В качестве альтернативы, я могу передать по значению, которое, кажется, охватывает также все предыдущие случаи:
auto foo(A a) { std::cout<<"A"<<std::endl; }
Демо Теперь, однако, большой объект должен быть скопирован (- по крайней мере, в принципе).
Есть ли элегантный способ обойти эти проблемы?
Помните, что я не могу изменить универсальную опорную функцию, поэтому SFINAE и тому подобное невозможно.
1 ответ
Честно говоря, я думаю, тебе здесь не повезло. Типичные подходы все терпят неудачу. Ты можешь сделать...
SFINAE?
template <typename T> auto foo(T&& );
template <typename T,
typename = only_if_is<T, A>>
auto foo(T&& );
foo(A{}); // error: ambiguous
Написать класс, который принимает ссылку l-or-rvalue?
template <typename T> lref_or_ref { ... };
template <typename T> auto foo(T&& );
auto foo(lref_or_ref<A> );
foo(A{}); // calls general, it's a better match
Лучшее, что вы могли бы сделать, это ввести функцию пересылки с помощью chooser:
template <int I> struct chooser : chooser<I - 1> { };
template <> struct chooser<0> { };
template <typename T>
auto bar(T&& t, chooser<0> ) {
// worst-option, general case
foo(std::forward<T>(t));
}
template <typename T,
typename = only_if_is<T, A>>
auto bar(T&& t, chooser<1>) {
// A-specific
}
template <typename T>
auto bar(T&& t) {
bar(std::forward<T>(t), chooser<20>{});
}
Но вы упомянули в комментарии, что это не работает для вас. Итак, я думаю, ваш единственный вариант: написать предложение в комитет по стандартам!
На самом деле, есть надежда! Если концепция будет принята (хорошая идея, ТартанЛлама!):
template <typename T>
requires IsForwardRefTo<T, A>
auto foo(T&& t) {
// since this is more constrained than the generic forwarding reference
// this one should be preferred for foo(A{})
}