Проверьте, имеет ли класс функцию-член заданной подписи - PullRequest
119 голосов
/ 18 сентября 2008

Я прошу шаблонный трюк, чтобы определить, имеет ли класс определенную функцию-член с данной сигнатурой.

Проблема аналогична приведенной здесь http://www.gotw.ca/gotw/071.htm но не то же самое: в пункте книги Саттера он ответил на вопрос, что класс C ДОЛЖЕН ПРЕДОСТАВЛЯТЬ функцию-член с определенной сигнатурой, иначе программа не будет компилироваться. В моей задаче мне нужно что-то делать, если у класса есть эта функция, иначе делать «что-то еще».

С аналогичной проблемой столкнулся boost :: serialization, но мне не нравится принятое ими решение: шаблонная функция, которая по умолчанию вызывает свободную функцию (которую вы должны определить) с определенной сигнатурой, если вы не определите конкретную функция-член (в их случае «сериализация», которая принимает 2 параметра заданного типа) с определенной сигнатурой, иначе произойдет ошибка компиляции. То есть реализовать как навязчивую, так и не навязчивую сериализацию.

Мне не нравится это решение по двум причинам:

  1. Чтобы не быть навязчивым, вы должны переопределить глобальную функцию «сериализации», которая находится в пространстве имен boost :: serialization, поэтому у вас есть В ВАШЕМ КЛИЕНТЕ КОДЕ, чтобы открыть повышение пространства имен и сериализацию пространства имен!
  2. стек для решения этой проблемы В беспорядке было от 10 до 12 вызовов функций.

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

Можете ли вы дать мне подсказку, чтобы решить эту загадку?

Ответы [ 14 ]

2 голосов
/ 18 сентября 2008

Хорошо. Вторая попытка Ничего страшного, если тебе это тоже не понравится, я ищу больше идей.

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

Как я уже сказал, возможно, вам не нужна дополнительная работа, связанная с классами «тегирования», которые реализуют этот член. В этом случае я смотрю на третье решение ....

1 голос
/ 09 июля 2017

Без поддержки C ++ 11 (decltype) это может работать:

SSCCE

#include <iostream>
using namespace std;

struct A { void foo(void); };
struct Aa: public A { };
struct B { };

struct retA { int foo(void); };
struct argA { void foo(double); };
struct constA { void foo(void) const; };
struct varA { int foo; };

template<typename T>
struct FooFinder {
    typedef char true_type[1];
    typedef char false_type[2];

    template<int>
    struct TypeSink;

    template<class U>
    static true_type &match(U);

    template<class U>
    static true_type &test(TypeSink<sizeof( matchType<void (U::*)(void)>( &U::foo ) )> *);

    template<class U>
    static false_type &test(...);

    enum { value = (sizeof(test<T>(0, 0)) == sizeof(true_type)) };
};

int main() {
    cout << FooFinder<A>::value << endl;
    cout << FooFinder<Aa>::value << endl;
    cout << FooFinder<B>::value << endl;

    cout << FooFinder<retA>::value << endl;
    cout << FooFinder<argA>::value << endl;
    cout << FooFinder<constA>::value << endl;
    cout << FooFinder<varA>::value << endl;
}

Как это, надеюсь, работает

A, Aa и B - это рассматриваемые предложения, Aa - это особое, которое наследует искомого члена.

В FooFinder true_type и false_type являются заменами для соответствующих классов C ++ 11. Также для понимания шаблонного метапрограммирования они раскрывают саму основу SFINAE-sizeof-трюка.

TypeSink - это структура шаблона, которая позже используется для погружения интегрального результата оператора sizeof в создание экземпляра шаблона для формирования типа.

Функция match - это еще один вид шаблона SFINAE, который не имеет общего аналога. Следовательно, он может быть создан только в том случае, если тип его аргумента соответствует типу, для которого он был специализирован.

Обе функции test вместе с объявлением enum, наконец, образуют центральный шаблон SFINAE. Существует универсальный, использующий многоточие, которое возвращает false_type, и аналог с более конкретными аргументами, чтобы иметь приоритет.

Чтобы иметь возможность создавать экземпляр функции test с аргументом шаблона T, необходимо создать экземпляр функции match, так как ее тип возврата требуется для создания экземпляра аргумента TypeSink. Предостережение заключается в том, что &U::foo, будучи заключенным в аргумент функции, является , а не , на который ссылаются из специализации аргумента шаблона, поэтому поиск унаследованного члена все еще происходит.

0 голосов
/ 15 апреля 2019

Если вы используете безумие Facebook, их макрос из коробки поможет вам:

#include <folly/Traits.h>
namespace {
  FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
} // unnamed-namespace

void some_func() {
  cout << "Does class Foo have a member int test() const? "
    << boolalpha << has_test_traits<Foo, int() const>::value;
}

Хотя детали реализации те же, что и в предыдущем ответе, использовать библиотеку проще.

0 голосов
/ 27 октября 2008

Я думаю, что вы ищете ответ здесь.

http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time

и чуть более полный пример здесь

http://pastie.org/298994

Я использую технику, чтобы обнаружить присутствие поддерживающего оператора ostream << </strong> в рассматриваемом классе, а затем генерирую другой бит кода в зависимости.

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

Бред

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...