C ++ Передача функциональных объектов в виде lvalues ​​и / или rvalues - PullRequest
0 голосов
/ 05 июля 2018

У меня есть класс, который должен фильтровать его содержимое в соответствии с предоставленным пользователем предикатом. Интерфейс, который мне дан, предписывает брать ссылку на предикат:

class Test {
  vector<int> data;
public:
  template <class PREDTYPE>
  void filter(PREDTYPE& pred) {
    return;
  }
};

Мне также дается фрагмент кода теста, который выглядит примерно так:

class Filter {
public:
  bool operator()(int) const {
    return false;
  }
};

int main() {
  Test test;
  test.filter(Filter());
}

Это не компилируется, говоря cannot bind non-const lvalue reference of type 'Filter&' to an rvalue of type 'Filter'. Если я изменю код тестирования на

int main() {
  Test test;
  Filter filter;
  test.filter(filter);
}

это работает, но это зависит от конечного пользователя, и я не могу контролировать его поведение. Я попытался перегрузить метод filter, создав версию, которая будет принимать предикат по значению, а затем передавать его по ссылке, но это также не компилируется с сообщением call of overloaded 'filter(Filter&)' is ambiguous.

Отсюда мой вопрос: возможно ли построить фильтр, который бы принимал и r-значения, и l-значения предиката?

Ответы [ 4 ]

0 голосов
/ 05 июля 2018

Это зависит от того, что функция делает с предикатом. Если он сохраняет ссылку / указатель на него после его возвращения, вы должны предоставить что-то, что будет жить достаточно долго. Однако, если он не хранит память передаваемого объекта предиката, вы можете привести свой предикат к lvalue:

test.filter(static_cast<Filter&>(Filter()));

Или обертка, которая приведена в короткой служебной функции:

template <class T>
T& stay(T&& a)
{ return a; }

test.filter(stay(Filter()));
0 голосов
/ 05 июля 2018

Короче говоря, да (C ++ 11)

Вам просто нужно положиться на правила свертывания ссылок, чтобы убедиться в этом:

template <typename PREDTYPE>
void filter(PREDTYPE&& pred) { // notice the &&
    // ... Whatever
    // Use perfect forwarding *IF* you can
    std::forward<PREDTYPE>(pred)(some_stuff);
    // Do not use pred after it has been forwarded!
}

Это будет принимать и rvalues, и lvalues ​​без необходимости полагаться на const - ссылку (так что ваши предикаты все еще могут быть изменяемыми). Если вы застряли на старых стандартах C ++, лучше всего вместо этого использовать константную ссылку (с оговоркой, выделенной выше) или встроить свой фильтр в std::function и полагаться на неявные преобразования на сайте вызова.

0 голосов
/ 05 июля 2018

Обычно функциональные объекты должны передаваться по значению. Так что не перегружайте и измените свою подпись на

template <class PREDTYPE> void filter(PREDTYPE pred)

Объяснение, почему это лучше, можно найти здесь .

0 голосов
/ 05 июля 2018

Возможно, вы захотите изменить подпись функции-члена на

template <class PREDTYPE> void filter(PREDTYPE&& pred)

Здесь предикат передается как универсальная ссылка, которая сворачивается в ссылку lvalue для lvalue, переданных в rvalue, ссылки на rvalue, переданные в.

...