Question

// 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 is always faster than version 1.

Fact is, getRange() is used by multiple classes. If I were to apply version 2, there would be a lot of code duplication.

Also, I cannot pass ranges as a non-const reference to getRange(), as some other classes use a std::stack instead of a std::vector. I would have to create multiple overloads and have more code duplications.

Is there a common way/idiom to emplace the return value?

Was it helpful?

Solution

Following our discussion in the comments about using SFINAE to allow emplacement on any type of container (whether it supports emplace or emplace_back), here is an example implementation.

You just need a way to detect whether emplace or emplace_back is available, and dispatch the call accordingly. For this purpose, we'll use 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)...);
}

Kudos to @DyP who provided this much nicer and shorter C++11 solution (see comments). The previous traits-based solutions (revisions 3 & 4) were a lot more verbose.


Using it is quite straightforward:

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>>();
}

I'll let you bridge the gap between my test_emplace() usage example and your actual code, but it shouldn't be too hard now. ;)

OTHER TIPS

Here is a way to you can pass the code to emplace into GetRange without knowing what it is you are emplacing into:

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 );
   } );
}

No, you are constructing with getRange() where as emplace_back has the construction done in the vector.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top