Проверка, существует ли функция без аппаратного члена с SFINAE - PullRequest
1 голос
/ 02 сентября 2011

Я хочу создать прокси для функций-членов и операторов.Они должны иметь одинаковые возвращаемый тип и параметры и должны подходить для нескольких классов, которые задаются как параметры шаблона.Даже если у класса нет конкретной функции-члена или оператора, я хочу, чтобы он компилировался, а не с ошибкой, по сути, с SFINAE.Если X имеет метод f(), а Y не имеет метода с именем f, мне нужно Proxy<X>, чтобы иметь также f(), который вызывает X::f(), и мне нужно Proxy<Y> дляскомпилируйте и создайте его без проблем.

Извлечение возвращаемого типа из известной функции больше не является проблемой после моего предыдущего вопроса .Однако, если такая функция отсутствует, она завершается ошибкой.

Я уже знаю несколько приемов метапрограммирования шаблонов, чтобы определить, существует ли данная функция, и включить определенную функцию, если они есть, однако все они работают только наимена произвольных функций вместо произвольных, что сильно ограничивает их использование в этом случае, поскольку мне нужна одна и та же конструкция для нескольких функций.

Мне нужно только проверить, существует ли какая-либо функция с данным именем, если они перегруженыварианты, которые мне не нужно проверять, существует ли конкретный, автоматическое вычитание шаблона решает это (или я так надеюсь)

Мой текущий код выглядит так:

template <class T>
class Proxy
{

    //  using my resultof solution
    template <class... Args>
    resultof(T::f, Args...) f (Args... x)
    {
        return x.f(x...);
    }

    //  using another return type extraction solution
    template <class... Args>
    typeof(T::f(std::declval<Args>()...)) f (Args... x)
    {
        return x.f(x...);
    }

    T x;

};

, который должен компилироваться дажеесли T не имеет функции с именем f.К сожалению, обе версии не работают с ошибкой.

Реализация результата:

#define resultof(f, ...) typeof(Param<__VA_ARGS__>::Func(f))

template <class... Args>
class Param
{

    public:

        template <class R>
        static R Func (R (*) (Args...));

        template <class R, class C>
        static R Func (R (C::*) (Args...));

        template <class R, class C>
        static R Func (R (C::*) (Args...) const);

};

Ответы [ 3 ]

3 голосов
/ 02 сентября 2011

Я подозреваю, что

template<typename... Args>
decltype( std::declval<T&>().f(std::declval<Args>()...) )
f(Args&&... args)
{
    return x.f(std::forward<Args>(args)...);
}

должен запускать SFINAE и отбрасывать любое создание экземпляра f, для которого возвращаемый тип имеет неправильную форму (например, неоднозначная или несуществующая перегрузка) вместо серьезной ошибки. Хотя я не совсем уверен, потому что T - это параметр proxy, а не f, и я просто не могу разобрать соответствующие части Стандарта (я верю около 14.8.2). Кажется, ни один из примеров, приведенных в ненормативных примечаниях, не подходит.

Если это не так, можно использовать

template<typename U = T&, typename... Args>
decltype( std::declval<U>().f(std::declval<Args>()...) )
f(Args&&... args)
{
    return x.f(std::forward<Args>(args)...);
}

, для которого мой компилятор с радостью принимает proxy<int> p;, в отличие от первого варианта. p.f(); приводит к ошибке «Не найдена соответствующая функция», как это обычно бывает в SFINAE.


Я рекомендую по возможности использовать автономную форму операторов:

template<typename T, typename U>
auto operator+(Proxy<T> const& lhs, Proxy<U> const& rhs)
-> decltype( std::declval<T const&>() + std::declval<U const&>() )
{
    return lhs.x + rhs.x;
}

это возможность.

1 голос
/ 02 сентября 2011

Вам необходимо изолировать проверку существования f в параметре шаблона proxy на дополнительный уровень. Следующее позволит вам звонить proxy<X>::f() любым способом, которым вы можете звонить X::f():

template<typename T,typename ... Args>
struct f_result
{
    typedef decltype(std::declval<T&>().f(std::declval<Args&&>()...)) type;
};

template<typename T>
struct proxy
{
    T val;

    template<typename ... Args>
    typename f_result<T,Args...>::type
    f(Args&& ... args)
    {
        return val.f(static_cast<Args&&>(args)...);
    }
};

Быстрый тест:

#include <iostream>

struct X
{
    void f()
    {
        std::cout<<"X::f()"<<std::endl;
    }

    int f(int i)
    {
        std::cout<<"X::f("<<i<<")"<<std::endl;
        return i;
    }
};

struct Y
{};

struct Z
{
    int f()
    {
        std::cout<<"Z::f()"<<std::endl;
        return 42;
    }
};

int main(int, char**)
{
    proxy<X> px;
    px.f();
    int i=px.f(3);
    std::cout<<"i="<<i<<std::endl;

    proxy<Y> py;
    proxy<Z> pz;
    int j=pz.f();
    std::cout<<"j="<<j<<std::endl;
}

Это нормально работает с g ++ 4.5 и g ++ 4.6 в режиме -std = c ++ 0x.

1 голос
/ 02 сентября 2011

На первый взгляд это кажется тривиальным:

template <typename T> class Proxy : public T { };

Ничто другое в C ++ не даст Proxy<T> всех членов T для любого T. Единственный недостающий бит - это ctors, но из вашего вопроса я делаю вывод, что вы уже знаете, как их перенаправить.

Справочная информация: На практике набор возможных имен членов T бесконечен. Следовательно, вы не можете найти .f() по имени в Proxy<T>, и единственная другая область, в которой ищется имя члена, - это область базового класса.

...