0

While trying to optimize a library I wrote, I came across an issue that I cannot seem to solve.

Consider the wrapper struct below (simplified):

template <typename T>
struct MyPair {
    T data;
    // some other stuff here
};

Perfectly forwarding a bunch of MyPairs is not an issue. But if I try to forward its member variable data for example into a tuple (could be others as well), it does not perfectly forward it but rather calls the move constructor instead:

template <typename... Ts>
struct Tuple {
   public:
    template <typename... Pairs>
    Tuple(Pairs&&... pairs)
        : data_{std::forward<Ts>(pairs.data)...} {} // Not forwarding, calling move instead

   private:
    std::tuple<Ts...> data_;
};

The full working example can be found here. The output indicates that a move constructor is called for each pair we will add.

I have tried other approaches such as forwarding the pair itself before accessing the member [1]. I have also tried alternatives but to no avail [2].

I would appreciate any help that allows us to get rid of unnecessary moves operations.

8
  • Perfect forwarding just means that the value category is forwarded. So a move is generally what you expect. There are sutiations where the move can be elided completely, but the requirements for when the compiler is allowed to do that kind of optimization is a different thing. Commented Mar 25, 2024 at 20:08
  • 1
    std::forward<Pairs>(pairs).data and watch the magic happen. Member access respects value category too (not by chance, it was designed with forwarding in mind). Commented Mar 25, 2024 at 20:10
  • Oh, I answered one of the Q&As you linked (and that say the same thing essentially). How did you "try" it? Because it's specified to work, and it will be more prudent to show how you did it (likely with a mistake) than describe yourself doing it and assuming it "just failed". Commented Mar 25, 2024 at 20:13
  • @super The thing that eludes me is that if I decide to store the whole instead of just data, I do not need any move at all. The value is perfectly forward with zero additional costs. The moment I decide to not store the whole Pair but just its value data, the compiler adds an additional move Commented Mar 25, 2024 at 20:50
  • 1
    It performs a copy because the member is a reference (a detail that should be in a minimal reproducible example in the post itself, not in godbolt that was blocked on the network I accessed from before). timsong-cpp.github.io/cppwp/n4868/expr.ref#6.sentence-1 - Reference members will always be forwarded as lvalues by standard machinery here. And there's no way to detect it's a member reference (decltype being the odd beast that it is). Commented Mar 25, 2024 at 22:05

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.