What's the correct `enable_if` constraint on perfect forwarding setter?

前端 未结 3 1777
孤街浪徒
孤街浪徒 2020-12-28 19:05

Herb Sutter\'s Back to the Basics! Essentials of Modern C++ presentation at CppCon discussed different options for passing parameters and compared their performance

3条回答
  •  有刺的猬
    2020-12-28 19:09

    I tried to fit these thoughts in a comment, but they would not fit. I presume to write this as I am mentioned both in the comments above, and in Herb's great talk.

    Sorry to be late to the party. I have reviewed my notes and indeed I am guilty of recommending to Herb the original constraint for option 4 minus the errant !. Nobody is perfect, as my wife will assuredly confirm, least of all me. :-)

    Reminder: Herb's point (which I agree with) is start with the simple C++98/03 advice

    set_name(const string& name);
    

    and move from there only as needed. Option #4 is quite a bit of movement. If we are considering option #4, we are down to counting loads, stores and allocations in a critical part of the application. We need to assign name to name_ just as fast as possible. If we are here, code readability is far less important than performance, though correctness is still king.

    Providing no constraint at all (for option #4) I believe to be slightly incorrect. If some other code attempted to constrain itself with whether or not it could call employee::set_name, it could get the wrong answer if set_name is not constrained at all:

    template 
    auto foo(employee& e, String&& name) 
    -> decltype(e.set_name(std::forward(name)), void()) {
        e.set_name(std::forward(name));
        // ...
    

    If set_name is unconstrained and String deduces to some completely unrelated type X, the above constraint on foo improperly includes this instantiation of foo in the overload set. And correctness is still king...

    What if we want to assign a single character to name_? Say A. Should it be allowed? Should it be wicked fast?

    e.set_name('A');
    

    Well, why not?! std::string has just such an assignment operator:

    basic_string& operator=(value_type c);
    

    But note that there is no corresponding constructor:

    basic_string(value_type c);  // This does not exist
    

    Therefore is_convertible{} is false, but is_assignable{} is true.

    It is not a logic error to try to set the name of a string with a char (unless you want to add documentation to employee that says so). So even though the original C++98/03 implementation did not allow the syntax:

    e.set_name('A');
    

    It did allow the same logical operation in a less efficient manner:

    e.set_name(std::string(1, 'A'));
    

    And we are dealing with option #4 because we are desperate to optimize this thing to the greatest degree possible.

    For these reasons I think is_assignable is the best trait to constrain this function. And stylistically I find Barry's technique for spelling this constraint quite acceptable. Therefore this is where my vote goes.

    Also note that employee and std::string here are just examples in Herb's talk. They are stand-ins for the types you deal with in your code. This advice is intended to generalize to the code that you have to deal with.

提交回复
热议问题