Можно ли основать специализацию шаблона на наличии или отсутствии определенного аргумента в сигнатуре метода? - PullRequest
3 голосов
/ 26 января 2012

Рассмотрим следующий класс:

class MyClass
{
public:
  template<class T> typename T::result_type apply(T& func)
  {
    if (is_int())
    {
      return func(int(0));
    }
    return func(double(0));
  }
  ...
};

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

В любом случае, типичный функтор будетбыть примерно таким:

struct MyFunc
{
  typedef void result_type;
  template<class V> void operator()(V)
  {
    // do something
  }
};

И можно было бы использовать это так:

MyClass c;
MyFunc f;
c.apply(f);

У меня такой вопрос - можно ли изменить MyClass::apply, чтобы распознать немного другую версиюfunctor в дополнение к исходному , например, тот, который ожидает, что ссылка на объект вызывающей стороны будет передана вместе со всеми другими параметрами, примерно так:

struct MyFuncEx
{
  typedef void result_type;
  template<class V> void operator()(const MyClass& caller, V)
  {
    // do something
  }
};

Итак, следующеекод также будет компилироваться:

MyClass c;
MyFunc f;
c.apply(f);
MyFuncEx f2;
c.apply(f2);

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

1 Ответ

3 голосов
/ 26 января 2012

Это действительно зависит от того, есть ли у вас C ++ 11 или нет. Это должно решить проблему перегрузки в C ++ 11 (*):

class MyClass {
private:
  int _int;
  double _double;

public:
  template <typename F>
  auto apply(F& f) -> decltype(f(_int), f(_double)) {
    if (is_int()) { return f(_int); }
    return f(_double);
  }

  template <typename F>
  auto apply(F& f) -> decltype(f(*this, _int), f(*this, _double)) {
    if (is_int()) { return f(*this, _double); }
    return f(_double)
  }

};

Как это работает?

  • Устранение неподходящих перегрузок: спецификация типа конечного возврата с decltype создает неоцененный контекст. Компилятор выполняет регулярное разрешение перегрузки выражения, но заботится только о типе. Если происходит ошибка (operator() не существует в f), тогда мы нажимаем SFINAE, и эта перегрузка apply отбрасывается
  • Неоднозначность, если оба работают: если обе перегрузки подходят (потому что F предоставляет оба оператора), то вызов неоднозначен

(*) Я не слишком уверен в правильности спецификации типа трейлинг-возврата и, более конкретно, в использовании this и _arg. Ошибка Clang 3.0 и gcc 4.5.2 . Это можно обойти, просто станьте более многословным.

// first
decltype(f(0), f(0.0))

// second
decltype(f(std::declval<MyClass>(), 0), f(std::declval<MyClass>(), 0.0))

В действии на ideone с этим обходным путем:

РЕДАКТИРОВАТЬ: справиться с требованием int/double.

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