2011-01-10 4 views
3

У меня есть std :: set из std :: pairs, а вторая пара строка. Я хочу проверить, существует ли пара в наборе.std :: set <std :: pair <size_t, std :: string>> :: find(), без построения строковой копии

 
std::set< std::pair<size_t, std::string> > set_; 

bool exists(size_t x, const std::string& s) 
{ 
    std::set< std::pair<size_t, std::string> >::iterator i = set_.find(std::make_pair(x, s)); // copy of s is constructed by make_pair! 
    return i != set_.end(); 
} 

Я называю эту функцию часто (да, очень часто), поэтому я хочу, чтобы выполнить эту проверку, не создавая временную копию строки. Есть ли способ сделать это так же просто и красно, как то, что у меня есть здесь, но которое не создает временную копию строки? Любое решение с контейнерами STL или Boost было бы неплохо.

+1

Вы делаете это неправильно. Напишите код, который прямолинейный, чистый и работает. Затем пройдите по линии, когда вы закончите, * профиль * ваше приложение и оптимизируйте то, что говорят результаты, медленные; не догадаться. – GManNickG

+0

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

+1

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

ответ

5

Используйте указатель на строку и переопределить предикат меньше (см конструктора станд :: набор)

+0

Я думаю, что это, наверное, лучший ответ. Я использую C++ 03 (нет ссылок на C++ 0x r-value, поэтому не могу использовать unique_ptr), и да, я хочу сохранить время O (log n). Это будет беспорядочно, но C++ запутан. –

+1

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

+0

Да, это правда. –

2

ли профилирование на самом деле показывает, что строка копия является серьезной проблемой здесь?

Если да, то можете ли вы изменить функцию существования, чтобы он принимал pair вместо двух аргументов и упорядочивал строку, которая должна быть построена непосредственно в паре, а не отдельно?

Если вы не можете этого сделать, вы всегда можете использовать shared_ptr<std::string> в качестве второго элемента вашего pair и придумать функцию сравнения, которая сравнивает строки из адресов, а не строки значений.

+0

или 'unique_ptr', который немного легче на ресурсах – rubenvb

+0

@rubenvb: Если' unique_ptr' доступен, так или иначе. Это зависит от ссылок rvalue, которые еще не находятся в стандарте и не реализованы во всех современных компиляторах. –

+0

@David: true, но это действительно лучше в простых случаях использования ';)' – rubenvb

1

Вы всегда можете найти себя.

static pair<size_t, std::string> helper(0,""); 
typedef std::set< std::pair<size_t, std::string> >::iterator iterator_type; 
helper.first = x; 
for (iterator_type i = set_.lower_bound(helper); i != set_.end(); ++i) { 
    if (i->first != x) 
     return false; 
    if (i->second == s) 
     return true; 
} 
return false; 
+1

Но, в отличие от встроенной находки, это не имеет «log (n)» времени поиска. – ltjax

+0

@ltjax: я отредактировал ответ так, чтобы он пропускал часть с правильной частью size_t в 'log (n)' время поиска. Однако поиск в этой части все еще линейный. Если на size_t всего несколько строк, это будет не заметно. – etarion

1

К сожалению, вы не можете сделать это в стандартной библиотеке C++, не изменяя key_type к чему-то ссылке-как. Существуют и другие библиотеки контейнеров, которые имеют функцию поиска с параметрами шаблонов, которая позволяет использовать различные типы поиска и компараторы (например, Boost.Intrusive). Помимо этого, вы можете просто надеяться, что оптимизатор удалит построение копии. (! Benchmark)

-1

Написать функтор, который держит ссылку целевой строки:

struct match_str : public std::unary_function<bool, std::string> 
{ 
    match_str(const std::string& s) : s_(s) {}; 
    bool operator()(const std::pair<size_t, std::string>& rhs) const 
    { 
    return rhs.second == s_; 
    } 
}; 

Использование:

std::set< std::pair<size_t, std::string> >::iterator i = std::find_if(set_.begin(), set_.end(), match_str(s)); 
+0

Удалить копию и обменять алгоритм 'O (log n)' на 'O (n)' one? Я думаю, что это сделает все медленнее, а не быстрее. – ltjax

+0

@ltjax: Итак? @ Джеймс Брок попросил «Любые решения», которые не копировали строки. Это делает это. –

+0

Здравый смысл! Не делайте алгоритм хуже в вопросе оптимизации ;-) – ltjax

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