template instantiation check for member existing i

2019-02-15 00:06发布

问题:

I have a group of classes that have one or more members of the type memberA, memberB, memberC. Not all classes have all the members. I want to create a template that will set the members such as

template <typename T>
void setAttributes(t & myClass, typeA memA, typeB memB, typeC memC)
{
  myClass.memberA = memA;
  myClass.memberB = memb;
  myClass.memberC = memC;
}

Obviously this will fail at compile time when attempting to instantiate a class that is missing one of the members. Is there a #if or something similar that will allow the check to be done for a conditional compile of the form

#ifdef myClass.memberA
  myClass.memberA = memA;
#endif

I do not have access right now to actually try this or anything similar and I would like to know if there is a valid way of setting it up.

I have seen references to SFINAE ("substitution failure is not an error.") but I am not sure how it would be used in this case. Is the suggestion below correct?

Substitution failure is not an error example would seem to imply that I should create a separate function for each member with a duplicate function without that member.

template <typename T>
void setMemberA(T & myClass, typeA memA)
{
  myClass.memberA = memA;
}

template <typenum T>
void setMemberA(T & myClass)
{
  // This is a dummy template to avoid a compilation problem
}

回答1:

Here's a possible set_memberA_if_exists implementation:

namespace details {
    template<class T>
    auto set_memberA_if_exists_impl(T & myClass, typeA memA, int) 
         -> decltype(myClass.memberA = memA, void()) {
        myClass.memberA = memA;
    }

    template<class T>
    void set_memberA_if_exists_impl(T & myClass, typeA memA, long) {}
}

template<class T>
void set_memberA_if_exists(T & myClass, typeA memA) {
     details::set_memberA_if_exists_impl(myClass, memA, 0);
}

Explanation:

SFINAE applies only to the signature, not the body, of the function template, so the trick is to encode the check inside the function template signature. This is easy using a C++11 trailing return type - -> decltype(myClass.memberA = memA, void()). If the expression myClass.memberA = memA would not compile, then it causes a substitution failure and removes the function template from the overload set. So that a call to set_memberA_if_exists_impl will still compile in that case, we also provide another do-nothing overload.

We also need a way to distinguish between those two overloads when they are both viable. This is done by introducing a third parameter. The do-something overload's third parameter's type is int, while the do-nothing overload's is long. By providing 0 (an int) as the third argument when we call it, we make sure that the do-something overload is preferred when it is viable.