2013-08-23 3 views
1
// VERSION 1 
struct Range { int begin, end; }; 
inline Range getRange() 
{ 
    int newBegin, newEnd; 
    // do calculations 
    return {newBegin, newEnd}; 
} 
struct Test 
{ 
    std::vector<Range> ranges; 
    inline void intensive() 
    { 
     ranges.push_back(getRange()); 
     // or ranges.emplace_back(getRange()); 
     // (gives same performance results) 
    } 
}; 

// VERSION 2 
struct Range { int begin, end; }; 
struct Test 
{ 
    std::vector<Range> ranges; 
    inline void intensive() 
    { 
     int newBegin, newEnd; 
     // do calculations 
     ranges.emplace_back(newBegin, newEnd); 
    } 
}; 

Version 2 всегда быстрее, чем версии 1.Есть ли способ заменить возвращаемое значение?

Факт, getRange() Используется несколькими классами. Если бы я применил версии 2, было бы много дублирования кода.

Кроме того, я не могу передать ranges как неконстантная ссылку на getRange(), так как некоторые другие классы используют std::stack вместо std::vector. Мне пришлось бы создавать множественные перегрузки и иметь больше дубликатов кода.

Есть ли общий способ/идиома до заменить возвращаемое значение?

+0

Попробуйте возвращающий ссылку г-значение из 'функции getRange'. –

+4

«Версия 2 всегда быстрее версии 1.» - и это утверждение основано на ... что? – Xeo

+5

@MohammadAliBaydoun: Нет, полная остановка, ветер назад и снова подумайте. – Xeo

ответ

4

После обсуждения в комментариях об использовании SFINAE для размещения на любом типе контейнера (независимо от того, поддерживает ли он emplace или emplace_back), приведен пример реализации.

Вам просто нужен способ определить, есть ли emplace или emplace_back, и отправьте вызов соответствующим образом. Для этой цели мы будем использовать SFINAE:

namespace detail 
{ 
    template<typename T, typename... Args> 
    auto emplace_impl(int, T& c, Args&&... pp) 
     -> decltype(c.emplace_back(std::forward<Args>(pp)...)) 
    { 
     return c.emplace_back(std::forward<Args>(pp)...); 
    } 

    template<typename T, typename... Args> 
    auto emplace_impl(long, T& c, Args&&... pp) 
     -> decltype(c.emplace(std::forward<Args>(pp)...)) 
    { 
     return c.emplace(std::forward<Args>(pp)...); 
    } 
} // namespace detail 

template<typename T, typename... Args> 
auto emplace(T& c, Args&&... pp) 
    -> decltype(detail::emplace_impl(0, c, std::forward<Args>(pp)...)) 
{ 
    return detail::emplace_impl(0, c, std::forward<Args>(pp)...); 
} 

Престижность @DyP, который предоставил эту гораздо лучше и более короткое решение C++ 11 (см комментарии). Предыдущие решения на основе признаков (ревизии 3 & 4) были намного более подробными.


С его помощью довольно прост:

template<typename Container> 
void test_emplace() 
{ 
    Container c; 
    emplace(c, 3); 
} 

int main() 
{ 
    test_emplace<std::queue<int>>(); 
    test_emplace<std::stack<int>>(); 
    test_emplace<std::deque<int>>(); 
    test_emplace<std::list<int>>(); 
    test_emplace<std::vector<int>>(); 
} 

Я дам вам преодолеть разрыв между моим test_emplace() примером использования и вашим реальным кодом, но он не должен быть слишком жестким Теперь. ;)

+1

* Пожалуйста, * проверьте мой ответ [здесь] (http://stackoverflow.com/a/9154394/500104) о том, как правильно проверить, можно ли вызывать функцию с определенными параметрами в C++ 11. – Xeo

+2

«Поскольку для стандартных контейнеров требуется, чтобы их элементы были« CopyConstructible »... Нет, это не так. 'std :: vector >' отлично. –

+1

Не проверяйте наличие элемента с определенной подписью. Проверьте правильность выражения (т. Е. Используйте 'decltype (std :: declval() .emplace (std :: declval ()))) –

1

Нет, вы сооружают с getRange() где, как emplace_back имеет конструкцию сделано в vector.

+0

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

+0

[Это то, что я получил до сих пор] (http://pastie.org/8262970), он работает, но я уверен, что это может быть лучше. Любая идея о том, как его улучшить? –

3

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

template<typename Emplacer> 
void GetRange(Emplacer emplace) { 
    int beg, end; 
    // ... 
    emplace(beg, end); 
} 

std::vector<Range> ranges; 
inline void intensive() 
{ 
    GetRange([&](int b, int e) { 
    ranges.emplace_back(b, e); 
    }); 
} 
+0

Ницца, +1. Могло бы быть немного громоздким, чтобы использовать лямбду на каждом сайте вызова, хотя мне все же нравится дизайн. – syam

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