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{})
}
Другие вопросы по тегам