0

I wonder that if the temporary object(name_compare()) is passed by value or by reference to std::sort.

I would be thankful for any hint on this question.

 struct Record {
        string name;
        // ...
    };
    struct name_compare {   // compare Records using "name" as the key
        bool operator()(const Record& a, const Record& b) const
            { return a.name<b.name; }
    };
    void f(vector<Record>& vs)
    {
        sort(vs.begin(), vs.end(), name_compare());
        // ...
    }  
sunshilong369
  • 646
  • 1
  • 5
  • 17
  • You need to see how [`std::sort`](https://en.cppreference.com/w/cpp/algorithm/sort) is declared. Does it declare the argument to be a value or a reference? – Some programmer dude May 23 '20 at 10:17
  • 1
    And please tell us *why* you wonder? Is there an underlying problem that makes you ask this question? What is the underlying problem? Please always ask about those directly. – Some programmer dude May 23 '20 at 10:18
  • The version of `std::sort()` that accepts three arguments that consist of a pair of iterators specifying a range and a comparator accepts all three arguments by value. – Peter May 23 '20 at 10:18
  • to pass by reference you have to have a declared object, here `name_compare()` is not declared, hence it will be moved and taken by value. – asmmo May 23 '20 at 10:21
  • And I have a question for you. Why haven't you accepted Vlad's answer to the question you just asked? I see it answers your last question. How do you wait for an answer although you won't accept it? – asmmo May 23 '20 at 10:24
  • @Some programmer dude I see.As per the documentaion aforementioned, i could draw the conclusion that it's passed by value.But it may cause extra waste(I mean it needs calll the default copy construction of `struct Record` in `std::sort` ).So, i halt between two opinions. – sunshilong369 May 23 '20 at 10:26
  • @asmmo Sorry, i am new to this community.I have followed your advice. – sunshilong369 May 23 '20 at 10:30
  • @sunshilong369 I'm not sure why you think passing the comparator by value means that the copy constructor for Record must be called. Those two things have no connection at all. – john May 23 '20 at 10:34
  • Just looking at the signature of [`std::sort`](https://en.cppreference.com/w/cpp/algorithm/sort) would tell you it's passed by value. There's no `&` or `&&` on the parameter declaration. – Blastfurnace May 23 '20 at 10:35
  • @asmmo You mean temporary object could not be passed by reference.Am i right? I would appreciate that if you could give me more information about it(related url is ok). Thank you for your attention. – sunshilong369 May 23 '20 at 10:37
  • @sunshilong369 A temporary object can be passed by reference. Happens all the time. – john May 23 '20 at 10:45
  • @john How and when?Could you give me a simple example? – sunshilong369 May 23 '20 at 10:47
  • Sure `void f(const std::string& s); f("abc");` a temporary `std::string` is constructed from `"abc"` and passed to the function `f` by reference. Note C++ does say that it must be a `const` reference for this to work. There's also the separate issue of r-value references. Those can also accept temporaries. – john May 23 '20 at 10:58
  • No it won't copy the `Record` structure, but it *can* cause a copy of the `name_compare` structure. And the compiler might omit (*elide*) that copy as well. – Some programmer dude May 23 '20 at 12:05

1 Answers1

0

std::sort deduces the Compare type and accepts it by-value:

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );

In most cases it means accepting the comparer by-value.

But that doesn't matter because

  1. The comparer is usually an empty object with just a operator(), so passing it is practically a no-op;
  2. The compiler can optimize away temporary object creation entirely;
  3. Even if it isn't empty, copying one object once is negligible in relation to the sorting operation that will follow

In your case struct name_compare has no members, and so is an empty object.

It is possible to force deduction of the comparer type as a reference by wrapping it in std::ref.

By the way, a comparer doesn't have to be a class, it can be a static or a friend function, too:

struct Record {
    string name;
    static bool compare(const Record& a, const Record& b) {
        return a.name < b.name;
    }
    void f(vector<Record>& vs) {
        sort(vs.begin(), vs.end(), Record::compare);
    }
};

But better yet, you can rely on the built-in std::less implementation and simply define operator< for your type:

struct Record {
    string name;
    bool operator< (const Record& that) const {
        return name < that.name;
    }
    void f(vector<Record>& vs) {
        sort(vs.begin(), vs.end()); // operator < is used automatically
    }
};
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • Defining `op<` that way might be wrong. Still, it is an optin we cannot reject out of hand. As an aside, if it is actually uncopyable, `std::ref()` comes to the rescue. – Deduplicator May 23 '20 at 10:40
  • @rustyx Since your answer, I could draw the conclusion that it needs call the default copy constructor of `struct Record` in `std::sort`.Am i right? – sunshilong369 May 23 '20 at 10:45
  • `std::sort` uses [`std::swap`](https://en.cppreference.com/w/cpp/algorithm/swap), which uses move-construction when possible. – rustyx May 23 '20 at 10:49
  • Thank you for your clarification.I would dig deep. – sunshilong369 May 23 '20 at 10:50