Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

It seems to me that in C++11 lots of attention was made to simplify returning values from functions and methods, i.e.: with move semantics it's possible to simply return heavy-to-copy but cheap-to-move values (while in C++98/03 the general guideline was to use output parameters via non-const references or pointers), e.g.:

// C++11 style
vector<string> MakeAVeryBigStringList();

// C++98/03 style
void MakeAVeryBigStringList(vector<string>& result);

On the other side, it seems to me that more work should be done on input parameter passing, in particular when a copy of an input parameter is needed, e.g. in constructors and setters.

My understanding is that the best technique in this case is to use templates and std::forward<>, e.g. (following the pattern of this answer on C++11 optimal parameter passing):

class Person
{
    std::string m_name;

public:   
    template <class T,
              class = typename std::enable_if
              <
                  std::is_constructible<std::string, T>::value
              >::type>
    explicit Person(T&& name) 
        : m_name(std::forward<T>(name)) 
    {
    }

    ...
};

A similar code could be written for setters.

Frankly, this code seems boilerplate and complex, and doesn't scale up well when there are more parameters (e.g. if a surname attribute is added to the above class).

Would it be possible to add a new feature to C++11 to simplify code like this (just like lambdas simplify C++98/03 code with functors in several cases)?

I was thinking of a syntax with some special character, like @ (since introducing a &&& in addition to && would be too much typing :) e.g.:

class Person
{
    std::string m_name;

public:   
    /*
       Simplified syntax to produce boilerplate code like this:

       template <class T,
              class = typename std::enable_if
              <
                  std::is_constructible<std::string, T>::value
              >::type>
    */
    explicit Person(std::string@ name) 
        : m_name(name) // implicit std::forward as well 
    {
    }

    ...
};

This would be very convenient also for more complex cases involving more parameters, e.g.

Person(std::string@ name, std::string@ surname)
    : m_name(name),
      m_surname(surname)
{
}

Would it be possible to add a simplified convenient syntax like this in C++? What would be the downsides of such a syntax?

share|improve this question
1  
Since you're using @ to represent perfect forwarding anyway, I'm pretty sure the std::forward<T> is redunant. (Especially since there's no T declared.) –  Mooing Duck Oct 30 '12 at 19:16
    
@MooingDuck: You're right. –  Mr.C64 Oct 30 '12 at 19:19
    
@MooningDuck: I've updated the code with the "hypothetical" simplified syntax according to that. –  Mr.C64 Oct 30 '12 at 19:32

2 Answers 2

up vote 1 down vote accepted

Sadly, no, because there's too many cases. In your sample, you use std::string@ to represent the perfectly forwarded type of an object that should be perfectly forwarded to a std::string constructor, and say "A similar code could be written for setters.". But you're wrong. You'd need another seperate syntax for assignment. For instance, I can construct a std::vector<anything> from an int, but I can't assign an int to a std::vector<anything>. So I'd need like std::vector<anything># for assignments. And what about the + operator? If I want to perfect forward a RHS to a member's operator+, then I'd need a notation for that too. And it can't be an existing symbol like + or that would make C++ much harder to parse than it already is! So you can see that this doens't apply universally how you appear to think it does.

Secondly, I disagree that the existing boilerplate doesn't scale well. It scales linearly, which is pretty well I think. (Note that the members and the mem-init-list boilerplate is required in any case and is thus not part of the scaling. Even if it were, that's still linear)

class Person
{
    std::string m_name;
    std::string m_address;
    std::string m_nickname;
    std::string m_phonenumber;
    std::string m_comment;

public:   
    template <class T, class U, class V, class W, class X,
              class = typename std::enable_if <
                  std::is_constructible<std::string, T>::value &&
                  std::is_constructible<std::string, U>::value &&
                  std::is_constructible<std::string, V>::value &&
                  std::is_constructible<std::string, W>::value &&
                  std::is_constructible<std::string, X>::value
              >::type>
    explicit Person(T&& name, U&& addr, V&& nick, W&& phone, X&& comment) 
        : m_name(std::forward<T>(name)), 
          m_address(std::forward<T>(addr)),
          m_nickname(std::forward<T>(nick)),
          m_phonenumber(std::forward<T>(phone)),
          m_comment(std::forward<T>(comment)),
    {
    }

    ...
};

Third: This is only needed when you need to pass an unknown type perfectly to the member, which is very rare. Normally, you'd just take all the members as std::string by value, and move them into the members, which is amazingly close to optimal considering how amazingly easy it is.

share|improve this answer
    
@MooningDuck: my understanding is that pass by value + move from the value is suboptimal with respect to the perfect forwarding solution. –  Mr.C64 Oct 30 '12 at 18:55
    
@Mr.C64: Yes, it can be suboptimal in some situations sometimes. Seriously, do it anyway. When the optimizer doesn't optimize it properly (which is rare), it is suboptimal by three pointer copies. Also, it's better code, which is far more important than a few measely pointer copies. Simpler Code = fewer mistakes = faster code. –  Mooing Duck Oct 30 '12 at 18:58
    
@MooningDuck: For a setter, would it be wrong for code like this void SetName(std::string@ name) { m_name = std::forward<T>(name); } to be expanded to template <class T, class = typename std::enable_if<std::is_constructible<std::string, T>::value>::type> void SetName(T&& name) { m_name = std::forward<T>(name);}? –  Mr.C64 Oct 30 '12 at 19:08
    
@MooningDuck: I agree with you that simpler code == fewer mistakes, that's why I was having that idea of syntax simplification (but I was not sure, and decided to ask others for insights - exchanging feedback in a polite way is a good thing). –  Mr.C64 Oct 30 '12 at 19:09
    
Setters don't have initializers. If you changed that to std::is_assignable and {m_name = std::forward<T>(Name);}, then it'd still be wrong, since you defined string@ as perfect forwarding of something constructable to a std::string, not assignable. As I said in the answer, int to std::string is the counterexample here. –  Mooing Duck Oct 30 '12 at 19:10

Frankly, this code seems boilerplate and complex

Probably because it's completely wrong. The "By value" part of that answer was what you were supposed to do. The other part, nobody ever does that. The only reason to do so was that you knew in advance your data member was non-movable (rather rare) and non-copyable and you didn't know what it could be constructed from, an extremely rare combination of circumstances. It doesn't even cover that circumstance, since it would have to be variadic.

class Person {
    std::string name;
public:
    Person(std::string cname)
        : name(std::move(cname)) {}
};

Done.

share|improve this answer
    
Also happens all the time if you don't know in advance what the type of the data member is. Like a container. Or an algorithm. –  Mooing Duck Oct 30 '12 at 18:43
    
Is no problem, because they all require movable. –  Scrubbins Oct 30 '12 at 18:45
    
@Scrubbins: It's not "completely wrong" (if you want to use this style, I'd say "You're completely wrong"), unless you think this answer is completely wrong: stackoverflow.com/a/10472297/1629821 –  Mr.C64 Oct 30 '12 at 18:45
    
The answer doesn't recommend this style at all. It merely suggests both options. It doesn't suggest which is preferable or why or when. –  Scrubbins Oct 30 '12 at 18:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.