Edytuj: opublikowałem własną odpowiedź, zachowałem pierwotną zaakceptowaną odpowiedź... skłonił mnie do myślenia o aliasach.
Edytuj: Moje pytanie dotyczy możliwości odróżnienia niejednoznaczności od istnienia elementu członkowskiego var/func w kontekście SFINAE (lub innym). Moje pytanie nie dotyczy tego, jak stworzyć szablon has_member, ale wykrycia różnicy między niejednoznacznością a istnieniem
Czy można skonfigurować częściowe specjalizacje, które będą się rozróżniać, gdy dostęp do elementu członkowskiego jest niejednoznaczny (obie podstawy klasy pochodnej mają element członkowski) vs, czy element w ogóle istnieje (żadna podstawa klasy pochodnej nie ma elementu członkowskiego) ? Muszę zwrócić wartość true tylko, jeśli zostanie wykryta niejednoznaczność, ale nie, jeśli w ogóle nie ma elementu członkowskiego lub jeśli istnieje tylko dla jednej klasy. To jest to, co mam do tej pory, które zwraca true dla niejednoznaczności (czego chcę), false dla tylko jednej klasy posiadającej składową (również to, czego chcę), ale zwraca true jeśli żadna klasa nie ma składowej (argh!)
//for the sake of this example, ClassOne comes from a lib I don't control
struct ClassOne {
//some unknown members in here...
};
struct ClassTwo {
string member_var;
};
template<typename A>
struct helper : std::true_type {};
template<typename A, typename B>
struct merged_class : public A, public B {};
template<typename T, typename = void>
struct has_member_var : std::true_type {
//Member request ambiguous or neither class has member.
//I want to catch these conditions separately,
//but this one template catches both :(
const int status = 1;
};
template<typename T>
struct has_member_var<
T
, typename std::enable_if<
//The next line results in either ambiguous member request error
//if both classes have the member OR
//member does not exist error if neither class has the member
//...how to tell the difference in the type of error?
helper<decltype(T::member_var)>::value
, T
>::type
> : std::false_type {
const int status = 2; //only one class has member
};
//This next one I have no idea how to do, if it's even possible.
//I'd like a third specialization that will match either the
//ambiguous condition or the member only existing in one of the
//base classes.
template<typename T>
struct has_member<
T
, typename std::enable_if<
some_freaky_magic<decltype(T::foo)>::true_value
, T
>::type
> : std::true_type {
const int status = 3;
};
Pożądane użycie:
switch(has_member<merged_class<ClassOne, ClassTwo>>::status) {
case 1:
cout << "member ambiguity";
break;
case 2:
cout << "member non-existence";
break;
case 3:
cout << "member existence for only one base";
break;
}
3 odpowiedzi
W porządku, myślę, że udało mi się wziąć wyrażenia SFINAE i dodaj dedukcję typu. Oto bardzo prymitywnie zhakowana odpowiedź, która wydaje się robić coś pożytecznego (patrz przykład użycia na dole).
(Prezentacja mogłaby być prawdopodobnie o wiele bardziej zwięzła i czysta, ale w ten sposób można ją podzielić na etapy).
Użycie: conflicting_X<A, B>::value
jest prawdziwe wtedy i tylko wtedy, gdy zarówno A
, jak i B
mają członka o nazwie x
i typ tego członka jest różny (ściśle, nie zepsuty). Pytania pośrednie, takie jak to, czy członek jest zdefiniowany dla obu klas, można rozstrzygnąć za pomocą conflicting_X<A, B>::both
i has_X<T>::value
.
#include <iostream>
#include <type_traits>
#include <typeinfo>
// has_X is taken straight from the other topic
template <typename T>
struct has_X
{
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
// Here we go...
template <typename T>
struct XType
{
typedef decltype(T::x) type;
};
template <bool, typename S, typename T>
struct compare_X
{
static const bool value = false;
};
template <typename S, typename T>
struct compare_X<true, S, T>
{
// Note that we don't decay, we want equality on the nose.
static const bool value = ! std::is_same<typename XType<S>::type, typename XType<T>::type>::value;
};
template <typename S, typename T>
struct conflicting_X
{
// We split this up so that XType is only instantiated if T::x really exists.
// You may also use conflicting_X::both as a useful datum.
static const bool both = has_X<S>::value && has_X<T>::value;
static const bool value = compare_X<both, S, T>::value;
};
/*** Example ***/
struct A { int x; };
struct B { int X; };
struct C { double x; };
void f(double) { }
int main() {
std::cout << has_X<A>::value << std::endl; // 1
std::cout << has_X<B>::value << std::endl; // 0
std::cout << "Conflict A/B? " << conflicting_X<A, B>::value << std::endl;
std::cout << "Conflict A/C? " << conflicting_X<A, C>::value << std::endl;
}
XType
i compare_X
i zastanawiałem się, czy byłoby możliwe wyizolowanie nazwy członka, zdefiniowanie dla niego aliasu, a następnie przekazanie tego aliasu do jednego has_member
, który nie trzeba było kopiować/wklejać dla każdej nowej nazwy członka.
AKTUALIZACJA: ostatnio zrobiłem więcej z kodem, który opublikowałem w mojej oryginalnej odpowiedzi, więc aktualizuję to, aby uwzględnić zmiany/dodatki.
Oto kilka fragmentów dotyczących użycia: * Wnętrzności tego wszystkiego są dalej niżej
Sprawdź członka x
w danej klasie. Może to być var, func, class, union lub enum:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
Sprawdź funkcję członka void x()
:
//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;
Sprawdź zmienną składową x
:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
Sprawdź klasę członka x
:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
Sprawdź związek członków x
:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
Sprawdź listę członków x
:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
Sprawdź jakąkolwiek funkcję członkowską x
niezależnie od podpisu:
CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
LUB
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
Szczegóły i rdzeń:
/*
- Multiple inheritance forces ambiguity of member names.
- SFINAE is used to make aliases to member names.
- Expression SFINAE is used in just one generic has_member that can accept
any alias we pass it.
*/
template <typename... Args> struct ambiguate : public Args... {};
template<typename A, typename = void>
struct got_type : std::false_type {};
template<typename A>
struct got_type<A> : std::true_type {
typedef A type;
};
template<typename T, T>
struct sig_check : std::true_type {};
template<typename Alias, typename AmbiguitySeed>
struct has_member {
template<typename C> static char ((&f(decltype(&C::value))))[1];
template<typename C> static char ((&f(...)))[2];
//Make sure the member name is consistently spelled the same.
static_assert(
(sizeof(f<AmbiguitySeed>(0)) == 1)
, "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
);
static bool const value = sizeof(f<Alias>(0)) == 2;
};
Makra (El Diablo!):
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member) \
\
template<typename T, typename = std::true_type> \
struct Alias_##member; \
\
template<typename T> \
struct Alias_##member < \
T, std::integral_constant<bool, got_type<decltype(&T::member)>::value> \
> { static const decltype(&T::member) value; }; \
\
struct AmbiguitySeed_##member { char member; }; \
\
template<typename T> \
struct has_member_##member { \
static const bool value \
= has_member< \
Alias_##member<ambiguate<T, AmbiguitySeed_##member>> \
, Alias_##member<AmbiguitySeed_##member> \
>::value \
; \
}
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_var_##var_name : std::false_type {}; \
\
template<typename T> \
struct has_member_var_##var_name< \
T \
, std::integral_constant< \
bool \
, !std::is_member_function_pointer<decltype(&T::var_name)>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \
\
template<typename T, typename = std::true_type> \
struct has_member_func_##templ_postfix : std::false_type {}; \
\
template<typename T> \
struct has_member_func_##templ_postfix< \
T, std::integral_constant< \
bool \
, sig_check<func_sig, &T::func_name>::value \
> \
> : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_class_##class_name : std::false_type {}; \
\
template<typename T> \
struct has_member_class_##class_name< \
T \
, std::integral_constant< \
bool \
, std::is_class< \
typename got_type<typename T::class_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_union_##union_name : std::false_type {}; \
\
template<typename T> \
struct has_member_union_##union_name< \
T \
, std::integral_constant< \
bool \
, std::is_union< \
typename got_type<typename T::union_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name) \
\
template<typename T, typename = std::true_type> \
struct has_member_enum_##enum_name : std::false_type {}; \
\
template<typename T> \
struct has_member_enum_##enum_name< \
T \
, std::integral_constant< \
bool \
, std::is_enum< \
typename got_type<typename T::enum_name>::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func) \
template<typename T> \
struct has_member_func_##func { \
static const bool value \
= has_member_##func<T>::value \
&& !has_member_var_##func<T>::value \
&& !has_member_class_##func<T>::value \
&& !has_member_union_##func<T>::value \
&& !has_member_enum_##func<T>::value \
; \
}
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member) \
CREATE_MEMBER_CHECK(member); \
CREATE_MEMBER_VAR_CHECK(member); \
CREATE_MEMBER_CLASS_CHECK(member); \
CREATE_MEMBER_UNION_CHECK(member); \
CREATE_MEMBER_ENUM_CHECK(member); \
CREATE_MEMBER_FUNC_CHECK(member)
Nie sądzę, żeby to było możliwe. Aby SFINAE działał tutaj, potrzebujesz jednego lub więcej wyrażeń, które są w określonej prawidłowej/nieprawidłowej kombinacji tylko wtedy, gdy element, którego szukasz, jest niejednoznaczny. Zakłada to rozszerzone SFINAE dla C++0x.
Jednak biorąc pod uwagę typ T
i potencjalny (nietypowy) element członkowski i
, jakie są dostępne wyrażenia? &T::i
i, jeśli ograniczysz sprawę tylko do członków danych, t.i
, gdzie t
ma typ T
.
W przypadku obu wyrażeń, jeśli i
jest niejednoznaczny, to każde z nich jest źle uformowane, ale jeśli jest obecne i nie jest niejednoznaczne, każde jest dobrze uformowane. Więc nie możemy spełnić Twoich wymagań.
Zauważ, że mając potencjalną bazę B
z jednoznaczną składową danych i
, możesz przetestować za pomocą &T::i
i t.B::i
. Jeśli pierwsze wyrażenie jest źle sformułowane, a drugie poprawnie, oznacza to, że istnieje niejednoznaczny element i
. To najbardziej zbliżone do twoich wymagań.
ClassTwo
(na przykład literówka lub w tym przypadku coś, co specjalnie przetestowałem). W tym momencie otrzymuję true
wyrzucony, kiedy powinienem otrzymać false
, i nie ma żadnej wskazówki, że żądany członek nigdzie nie istnieje:\
true
, gdy chcę false
. Najwyraźniej zachowuje się tak, jak powinien i po prostu nie ma sposobu na rozróżnienie wszystkich trzech warunków w tym kontekście :(
decltype
tak. Napiszesz odpowiedź?
Podobne pytania
Powiązane pytania
Nowe pytania
c++
C ++ to język programowania ogólnego przeznaczenia. Pierwotnie został zaprojektowany jako rozszerzenie C i ma podobną składnię, ale teraz jest to zupełnie inny język. Użyj tego tagu w przypadku pytań dotyczących kodu (który ma zostać) skompilowany za pomocą kompilatora C ++. Użyj znacznika specyficznego dla wersji w przypadku pytań związanych z określoną wersją standardu [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] lub [C ++ 23] itp. .
f(int, int); f(double, double); f(1, 1.5);
?WonderTwins
używa wielokrotnego dziedziczenia, więc pobiera zarównoZan
, jak ihero_power
członkówJayna
. Ta klasa jest przekazywana do szablonuevil_is_vanquished
, gdzieT::hero_power
powoduje błąd, ponieważ nie określiłem, z której klasy bazowej 'hero_power' pochodzi. Ten błąd powoduje, że drugi szablonevil_is...
jest ignorowany, co daje mi tylko pierwszy.class Wondertwins : public virtual Zan, public virtual Jayna { ... };
Jeśli musisz odziedziczyć inny typ o tej samej nazwie, może napisz do niego mały interfejs? (Edycja: Hmm… może to jednak nie jest właściwa rzecz.) Mam na myśli, jaki byłby zamierzony celWonderTwins::hero_power
? Przykład użycia?evil_is_vanquished
.std::is_same
na typach...