Я хотел бы скрыть по подписи, а не по имени в С++. Поэтому я написал макрос, который определяет функцию с переменным числом аргументов, которая делегирует все вызовы своему базовому классу, если они существуют. Я не могу использовать объявление using, потому что я не хочу, чтобы оно потерпело неудачу, если в базовом классе нет метода с таким именем, а унаследованные методы следует рассматривать только в том случае, если нет прямых совпадений членов. И это работает в большинстве случаев, потому что реализовано функцией с переменным числом переменных, которые всегда являются худшими кандидатами по сравнению с функциями без переменных. Но у меня проблема, когда дочерний класс тоже имеет вариативную функцию -> вызов становится неоднозначным.
Получается следующая ситуация (упрощенно - без сфинаев, макросов...):
#include <type_traits>
#include <iostream>
class A{
public:
void Do(){
std::cout << "A::Do()\n";
}
};
class B : public A
{
public:
template<
typename... TX,
typename SomeSFINAE = int
>
void Do(TX...){
std::cout << "B::Do()\n";
}
template<typename... T>
void Do(T...){
A::Do();
}
};
int main(){
B b;
b.Do();
return 0;
}
См. его на godbolt.
Я хотел бы решить эту ситуацию, не превращая один из методов в метод-диспетчер. Есть ли способ сделать один метод худшим кандидатом для решения этой неоднозначности?
Обновлять
Кажется, не совсем понятно, чего я действительно хочу достичь. Итак, вот псевдокод с комментариями:
#include <type_traits>
#include <iostream>
class A{
public:
void Do(){
std::cout << "A::Do()\n";
}
};
class B : public A
{
public:
template<
typename... TX
>
void Do(TX...){
std::cout << "B::Do()\n";
}
using A::Do; //<--- This should be considered only if no direct match is found in B
//Variadic function should win, because it is defined in B not in A - it should hide A.Do
//It should even work if A has NO method Do
};
int main(){
B b{};
b.Do(); //-> B::Do should be called, not A::Do
return 0;
}
Обновлять
Я хочу от вас чего-то похожего на то, как вы можете сделать обычную функцию худшим кандидатом только для функций с переменным числом аргументов.
Например:
#include <iostream>
void Do(int a){
std::cout << "better";
}
template<typename... T>
void Do(int a, T...){
//this is worse
std::cout << "worse";
}
int main(){
Do(42);
return 0;
}
Есть ли что-то, что может сделать вариативную функцию еще хуже?
Предыстория: в настоящее время у меня есть следующий макрос, просто чтобы эмулировать использование, как я этого хочу.
#define NATIVE_DO_NOT_HIDE_INHERITED_(AMETHOD, ...) \
private: template<typename $T, typename... $Args> \
using CallHiding$ ## AMETHOD = decltype(::std::declval<$T*>()->AMETHOD (::std::declval<$Args>()...)); \
\
public: template< \
typename... $Args \
, typename $Dependent = __VA_ARGS__ \
, bool $Detected = ::CORE_NATIVE_NS ::is_detected_v<CallHiding$ ## AMETHOD, $Dependent, $Args...> \
, typename = typename ::std::enable_if_t<$Detected > \
> \
constexpr decltype(auto) AMETHOD ($Args&&... args) \
{ \
/*allow virtual call*/ \
return static_cast<$Dependent*>(this) -> AMETHOD (::std::forward<$Args>(args)...); \
} \
\
private: template<typename $T, typename $FktArgsTuple, typename $ValueArgsTuple> \
class CallHidingGeneric$ ## AMETHOD : public ::std::bool_constant<false> {\
};\
\
private: template<typename $T, typename... $FktArgs, typename... $ValueArgs> \
class CallHidingGeneric$ ## AMETHOD<$T, ::std::tuple<$FktArgs...>, ::std::tuple<$ValueArgs...>> \
{\
template<typename AType> \
using ATemplate = decltype(::std::declval<AType>().template AMETHOD <$FktArgs...> (::std::declval<$ValueArgs>()...)); \
public: \
constexpr static bool value = ::CORE_NATIVE_NS ::is_detected_v<ATemplate, $T> ; \
}; \
\
public: template< \
typename... $FktArgs \
, typename... $Args \
, typename $Dependent = __VA_ARGS__ \
, typename = ::std::enable_if_t<(sizeof...($FktArgs) > 0)> \
, typename = ::std::enable_if_t< \
CallHidingGeneric$ ## AMETHOD<$Dependent, typename ::std::template tuple<$FktArgs...>, typename ::std::template tuple<$Args...>>::value \
> \
> \
constexpr decltype(auto) AMETHOD ($Args&&... args) \
{ \
return $Dependent ::template AMETHOD <$FktArgs...> (::std::forward<$Args>(args)...); \
}
#define NATIVE_DO_NOT_HIDE_INHERITED(AMETHOD) NATIVE_DO_NOT_HIDE_INHERITED_(AMETHOD, $Next)
#define NATIVE_DO_NOT_HIDE_INHERITED2(AMETHOD, ...) NATIVE_DO_NOT_HIDE_INHERITED_(AMETHOD, typename ::CORE_NATIVE_NS::type_container_t< __VA_ARGS__ >:: $Next)
Он отлично работает с обычными функциями, но функции, сгенерированные макросом, не считаются хуже...
using A::Do;
во втором обновлении,A::Do
будет скрытоB:Do
. Но я предполагаю, что ваша реальная ситуация более сложна, гдеA::Do
на самом деле является вариативным шаблоном, аB::Do
- другим вариативным шаблоном. Я думаю, вам нужно привести более реальный пример. - person JHBonarius   schedule 09.07.2020