Есть ли способ определить, существует ли функция и может ли она использоваться во время компиляции?

Edit: Короткий ответ на мой вопрос: у меня было ошибочное представление о том, что может делать SFINAE, и он вообще не проверяет тело функции:sfinae создает функциональное тело?

У меня есть проблема, похожая на эту:Можно ли написать шаблон для проверки существования функции?

Разница в том, что я хочу не только проверить, существует ли функция, но и узнать, действительно ли она пройдет SFINAE. Вот пример того, чего я пытаюсь достичь:

struct A
{
    void FuncA() { std::cout << "A::FuncA" << std::endl; }
};

struct B
{
    void FuncA() { std::cout << "B::FuncA" << std::endl; }
    void FuncB() { std::cout << "B::FuncB" << std::endl; }
};

template<typename T>
struct Inter
{
    void FuncA() { t.FuncA(); }
    void FuncB() { t.FuncB(); }

    T t;
};

// Always takes some sort of Inter<T>.
template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        // if( t.FuncA() exists and can be called )
            t.FuncA();

        // if( t.FuncB() exists and can be called )
            t.FuncB();
    }

    InterType t;
};

void DoEverything()
{
    Final<Inter<A>> finalA;
    Final<Inter<B>> finalB;

    finalA.CallFuncs();
    finalB.CallFuncs();
}

Обратите внимание, что в CallFuncs (), FuncA () и FuncB () всегда будут существовать, но они могут не компилироваться в зависимости от типа T, используемого в Inter. Когда я пытался использовать ответ в приведенном выше связанном вопросе, мне казалось, что он всегда дает мне истину, о которой я догадываюсь, потому что он только проверяет, существует ли функция, а не то, что она может быть скомпилирована (хотя я не могу править из-за того, что я что-то не испортил ...)

Для условного вызова функций, которые я представляю, я могу использовать enable_if как таковой:

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
    i.FuncA();
}

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
    i.FuncB();
}

template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        TryCallFuncA(t);
        TryCallFuncB(t);
    }

    InterType t;
};

но я не уверен, есть ли способ получить логическое значение для передачи в enable_if. Есть ли способ, которым я могу сделать это, или мне нужно прибегнуть к каким-то чертам типа, поддерживаемым вручную, которые указывают, существуют ли функции?

Что касается стоимости доступного набора функций C ++ 11, я использую MSVC 2010.

edit: Чтобы добавить важное примечание, в моей реальной ситуации реализация класса Inter фактически непрозрачна в момент, когда мне нужно определить, будет ли Inter :: FuncA / FuncB компилироваться, поэтому я не могу просто всплывать дочерние типы и проверьте наличие функции на них.

Ответы на вопрос(1)

Решение Вопроса

чтобы проверить это сейчас, но вы можете добавить специализациюFinal: template <typename T> struct Final< Inner<T> >; (что также помогает гарантировать, что тип всегдаInner, С этим вы можете извлечь тип, используемый для создания экземпляраInter.

Теперь вторая проблема - как использовать SFINAE, чтобы определить, существует ли функция-член. Я считаю, что это не должно быть слишком сложным (если вам не нужно делать это универсальным):

// Find out whether U has `void f()` member
template <typename U>
struct has_member_f {
    typedef char yes;
    struct no { char _[2]; };
    template<typename T, void (T::*)() = &T::f>
    static yes impl( T* );
    static no  impl(...);

    enum { value = sizeof( impl( static_cast<U*>(0) ) ) == sizeof(yes) };
};

Вы могли бы немного расширить это, чтобы сделать его немного более общим, но название функции, я думаю, вы не можете сделать универсальным. Конечно, вы можете написать это как макрос, который генерируетhas_member_##arg и использует&T:: arg, Тип члена, вероятно, легче обобщить ...

В качестве альтернативы, поскольку я не думаю, что это можно сделать универсальным, вы можете использовать хитрость внутриhas_member прямо в вашем типе: предоставить дваcallFuncA перегрузки, одна из которых связана с необязательным вторым аргументом с нужной подписью и по умолчанию имеет значение&T::FuncA который перенаправляет вызов, другой с многоточием, который является noop. затемcallFuncs назвал быcallFuncA а такжеcallFuncBи SFINAE отправит экспедитору или в полдень, и вы получите желаемое поведение.

template<typename T>
struct Final< Inter<T> >
{
    template <typename U, void (U::*)() = &U::FuncA>
    void callFuncA( Inter<T>* x ) {
        x.FuncA();
    }
    void callFuncA(...) {}

    void CallFuncs() {
        callFuncA(&t);                 // Cannot pass nonPOD types through ...
        // Similarly TryCallFuncB(t);
    }
    Inter<T> t;
};
 09 июн. 2012 г., 23:19
@Screndib: обнаружение основано на SFINAE, и это может только проверить на существование. Если участник недоступен (он является закрытым), он не будетsubstitution ошибка, и компилятор потерпит неудачу с ошибкой. В этом случае хитрость заключается в том, что вы не хотите проверятьInter<X>, скорееX (потому что в вашем случаеInter<T> всегда имеет обе функции, даже если они не компилируются. Сейчас еслиX само по себе создание экземпляра шаблона или простой класс на самом деле не имеет значения, как только создается экземпляр шаблона, он становится типом, совсем как (или, по крайней мере, очень близко) к классу, свернутому вручную.
 09 июн. 2012 г., 13:26
@MatthieuM .: Правильно, это решение только для C ++ 11. В C ++ вы не можете предоставить аргумент по умолчанию для шаблона функции. Это действительно приятная особенность, которая попала под радар в новом стандарте и требуется во многих случаях, когда стандарт требует, чтобы определенная функцияshall not participate in overload resolution unless X is true. (то есть уполномоченный SFINAE).
 09 июн. 2012 г., 23:22
... то есть, если ваш типFinal<Inter<A<X>>> это решение извлекаетA<X> блок и тесты там. Тот же трюк может быть применен рекурсивно, то есть вы можете добавить дополнительный уровень, чтобы удалитьA шаблон, извлечениеX и проверьте,X::f существует или нет.
 09 июн. 2012 г., 12:39
Если я правильно помню, существует проблема с аргументами по умолчанию и шаблонами функций в C ++ 03, которая сделала бы это решение доступным только в C ++ 11. Не могли бы вы подтвердить / слабый?
 Screndib09 июн. 2012 г., 20:17
К сожалению, это не поддерживается в VS (2010 или 2012). В целях понимания решения аргумент по умолчанию & amp; U :: FuncA проверяется не только на то, что он существует, но и на то, что если он вызывается, он будет правильно компилироваться (например, тип, предоставленный Inter & lt; T & gt;, сам может быть класс шаблона (например, struct A & lt; T & gt;) и реализация FuncA / FuncB зависит от этого типа)?

Ваш ответ на вопрос