0

For example, a class with two constructors, one taking a reference and the other taking a string r-value reference.

class A {
    public:

    A(std::string&& str) {
        Foo foo(std::move(str));
        //do something with foo. (e.g., use it to initialize some members)
        field = foo.bar();
    }

    A(const std::string& str) {
        Foo foo(str);
        //do something with foo. (e.g., use it to initialize some members)
        field = foo.bar();
    }

};

If these two constructors perform virtually the same thing besides std::move appearing in the move constructor, is there any way to avoid having two nearly identical chunks of code?

Steven Pham
  • 13
  • 1
  • 5

4 Answers4

2

Accept by value. That will do the trick of doing the same thing as both.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • This is just another way of explicitly invoking the copy constructor -- which could be fine, but be aware of it. – rivimey Jul 13 '17 at 14:29
  • @rivimey - You're unlikely to invoke the copy constructor on most compilers. Shouldn't even invoke the move...your temporary will be built right into the parameter. Your value has to have a copy or move constructor, but compilers can--and most do--remove the actual invocation. – Edward Strange Jul 13 '17 at 15:33
2

The first option is to use templated constructor:

class A
{
public:
    template<typename T, typename = std::enable_if<std::is_same<std::remove_reference<T>::type, std::string>::value>::type>
    A(T && str) {
         Foo foo(std::forward<T>(str));
        //do something
    }
};

But it looks a bit ugly. If Foo(const std::string &) actually makes a copy of string, then I'd prefer to pass the parameter by value:

class A
{
public:
    A(std::string str) {
         Foo foo(std::move(str));
        //do something
    }
};

The overhead of this approach is additional move constructor. But don't be bothered of it because the compiler is likely to optimize it.

Andrey Nasonov
  • 2,619
  • 12
  • 26
1

Obvious answer: make a third fn which performs the common part, which is called from both the other fns.

You may be able to directly call one from the other, but it would depend on the nature of the data stored within the class and what is being referenced. However, in many cases a copy constructor can be used to implement move, if desired, though not as efficiently.

rivimey
  • 921
  • 1
  • 7
  • 24
1

You can do this using universal references, but only in the cases where the constructor is templated.

class A {
public:
    template<typename T>
    A(T && str) {
        Foo foo(std::forward<T>(str));
        //do something
    }
};