Адаптация проверки существования функций-членов к аргументам функций - PullRequest
1 голос
/ 23 января 2011

Я пытался адаптировать это решение для обеспечения возможности существования обычных (не входящих в состав) функций.В моем случае, у меня есть много глобальных функций типа строковых утилит, которые принимают любой строковый тип T такой, что T имеет, скажем, char const* c_str() const функцию-члена.

Цель состоит в том, чтобы устранить странный компиляторсообщения об ошибках, если пользователь пытается передать некоторый тип T, который не имеет c_str() функции-члена, то есть вместо того, чтобы компилятор говорил «c_str (): нет такой функции-члена», я бы предпочел, чтобы компилятор сказал «foo (T &): нет такой функции "где foo - глобальная функция.

Вот адаптированный код:

template<bool B,typename T = void>
struct enable_if {
  typedef T type;
};

template<typename T>
struct enable_if<false,T> {
};

//
// This macro is adapted from the linked-to question -- this part works fine.
//
#define DECL_HAS_MEM_FN(FN_NAME)                                            \
  template<class ClassType,typename MemFnSig>                               \
  struct has_##FN_NAME##_type {                                             \
    typedef char yes[1];                                                    \
    typedef char no[2];                                                     \
    template<typename T,T> struct type_check;                               \
    template<class T> static yes& has( type_check<MemFnSig,&T::FN_NAME>* ); \
    template<class T> static no&  has( ... );                               \
    enum { value = sizeof( has<ClassType>(0) ) == sizeof( yes ) };          \
  }

// Declare an instance of the above type that checks for "c_str()".
DECL_HAS_MEM_FN(c_str);

// Define another macro just to make life easier.
#define has_c_str(STRINGTYPE) \
  has_c_str_type<STRINGTYPE,char const* (STRINGTYPE::*)() const>::value

//
// A "ValidatedStringType" is a StringType that uses the above machinery to ensure that
// StringType has a c_str() member function
//
template<class StringType>
struct ValidatedStringType {
  typedef typename enable_if<has_c_str(StringType),StringType>::type type;
};

// Here's the global function where I want to accept only validated StringTypes.
template<class StringType>
void f( typename ValidatedStringType<StringType>::type const& s ) {
}

struct S { // Class for testing that has c_str().
  char const* c_str() const {
    return 0;
  }
};

struct N { // Class for testing that does not have c_str().
};

using namespace std;

int main() {
  S s;
  N n;
  cout << has_c_str(S) << endl; // correctly prints '1'
  cout << has_c_str(N) << endl; // correctly prints '0'
  f( s ); // error: no matching function for call to 'f(S&)'
}

Однако, как показано выше, компилятор" не видит ""f(S&) - почему бы и нет?

Ответы [ 2 ]

1 голос
/ 23 января 2011

Если я правильно понимаю вопрос, применение enable_if к f само по себе похоже следующее решит проблему:

template<class StringType>
typename enable_if<has_c_str(StringType),StringType>::type
f( StringType const& s ) {....}

Надеюсь, это поможет.

0 голосов
/ 23 января 2011

Я не могу ответить на ваш вопрос («почему бы и нет?»), Но могу предоставить обходной путь. Но человек, это безобразно. В итоге вы определяете структуру и функцию для каждой функции:

template<class StringType, class T = typename ValidatedStringType<StringType>::type>
struct f_helper {
  void operator()( T const& s ) {
      // Put the contents of f() here
  }
};

template<class StringType>
void f(StringType const &s) {
  f_helper<StringType>()( s );
}

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

#define DECL_VALIDATED_FUNC( RetType, name, Validator, contents ) \
  template<class StringType, class T = typename Validator<StringType>::type> \
  struct name ## _helper { \
    RetType operator()( T const& s ) contents \
  }; \
  \
  template<class StringType> \
  RetType name(StringType const& s) { \
     name ## _helper<StringType>()( s ); \
}

DECL_VALIDATED_FUNC( void, f, ValidatedStringType, {
   // put contents of f() here
}) // note the closing parenthesis

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

template<class StringType, class T = typename ValidatedStringType<StringType>::type>
void f( T const& s ) {}
...