Is there a way to emplace return value?

1.3k views Asked by At
// 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?

3

There are 3 answers

14
syam On BEST ANSWER

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

2
Paul Evans On

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

1
Yakk - Adam Nevraumont On

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