2015-01-12 3 views
4

Я хотел бы найти максимальный Foo и позвонить по нему inc(), что является неконстантным методом. Конечно, при поиске max я не хочу создавать какие-либо копии или ходы, то есть я не хочу Foo foo = std::max(foo1, foo2). Я попробовал написать свой собственный макс, а g ++ настаивает на возврате константы &.Почему std :: max return by const &?

#include <iostream> 

class Foo 
{ 
public: 
    Foo(int x) : x_(x) { std::cout << "const" << std::endl; } 
    Foo(const Foo& foo) : x_(foo.x_) { std::cout << "copy const" << std::endl; } 
    Foo(Foo&& foo) : x_(foo.x_) { std::cout << "move const" << std::endl; } 
    bool operator< (const Foo& foo) const { return x_ < foo.x_; } 
    bool operator> (const Foo& foo) const { return x_ > foo.x_; } 
    void inc() { ++x_; } 
    int x_; 
}; 

/* 
* Doesn't compile. Must return const T& or must accept non-const T& 
* 
template<typename T> 
inline T& my_max(const T& f1, const T& f2) 
{ 
    return f1 > f2 ? f1 : f2; 
} 
* 
*/ 

int main() 
{ 
    Foo foo1(6);  
    Foo foo2(7);  
    Foo& foo = std::max(foo1, foo2); //Doesn't compile. Must be const Foo&. But then next line fails 
    foo.inc(); 
    std::cout << foo.x_ << std::endl; 
    return 0; 
} 
+2

Он возвращает константную ссылку, чтобы избежать ненужного копирования, потому что он еще до ссылки RValue и двигаться семантику. Вероятно, он будет определен по-разному, если его создать сегодня, например. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2199.html –

+0

Другим вариантом было бы добавить 'Foo copy() const & {return * this; } ', поэтому вы можете написать' std :: max (fooA, fooB) .copy(). inc(); '. Иногда это боль, когда у конструктора копирования нет имени. – MSalters

+1

Вы уже знаете один ответ: Ваш комментарий сказал * «... или должен принять неконстантный T & ...» *. Ваши две переменные, 'foo1' и' foo2', не являются константами lvalues, как и переменная, которую вы хотите использовать для хранения max, 'Foo & foo'. Так почему вы вводите 'const' где угодно? Просто оставьте 'const' от вашего' max' - хотя, возможно, вы можете переименовать его 'max_lvalue_ref', чтобы четко указать, что он делает. –

ответ

2

У вас есть 2 проблемы здесь:

  1. Отсутствующие константный спецификатор в результате
  2. опасно возвращать ссылку на константную ссылку параметра

В таком случае:

Foo& foo = std::max(Foo(6), Foo(7)); 

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

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

+0

'std :: max' возвращает' const & '. И, похоже, вы упустили вопрос? – Yakk

+0

ну, на момент написания был ответ без const для аргументов, которые делали то, что хотел OP, поэтому я не упомянул об этом - и это все еще хорошее решение, если OP не использует C++ 11. И да, std :: max возвращает const &, а также опасно: const Foo & f1 = std :: max (Foo (1), Foo (2)); const Foo & f2 = std :: max (Foo (3), Foo (4)); f1.f здесь 4 после второго оператора. Как OP упомянутые классы, и не понимаю, почему мы не могли использовать & вместо const, и я хотел предупредить об этой проблеме. – ISanych

2
template<typename T> 
T my_max(T&& f1, T&& f2) { 
    return std::forward<T>(f1 > f2 ? f1 : f2); 
} 

вышеупомянутое относительно твердое и сделает то, что вам нужно. Для этого требуется, чтобы два параметра имели одинаковый параметр r/l/const, который std::max не имеет. Вот почему max использует const&.

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

Так что не обманывайтесь отсутствием & в обратном значении выше: в вашем прецеденте, приведенное выше возвращает ссылку. Если прошло rvalues, оно возвращает значение.

Произошла ошибка в super_max, которая, если передана lvalues ​​того же типа, возвращает lvalue. Если прошло два различных типа, или RValue, возвращает копию:

template<class A, class B> 
struct max_return:std::common_type<A,B>{}; 
template<class A> 
struct max_return<A&,A&>{ 
    using type=A&; 
}; 
template<class A, class B> 
using max_return_t = typename max_return<A,B>::type; 

template<class T, class U> 
max_return_t<T,U> super_max(T&& t, U&& u) { 
    if (t < u) 
    return std::forward<U>(u); 
    else 
    return std::forward<T>(t); 
} 

также использует только <, и предпочитает левую часть на галстуке.

live example

Смежные вопросы