Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning non-copyable non-movable object with explicit constructor

Tags:

c++

c++11

If we have a non-movable, non-copyable class with non-explicit constructor, we can return it and use as follows (in C++11):

#include <iostream>
class NonCop
{
public:
    /*non explicit*/ NonCop(int a, int b) : number(a + b) {}
    NonCop(const NonCop&) = delete;
    int number;
};

NonCop get_non_cop()
{
    return {1, 2};
}

int main()
{
    NonCop &&nc = get_non_cop();
    std::cout << "three: " << nc.number << std::endl;
    return 0;
}

However if the constructor is explicit, it doesn't work. Is there any method of doing this in C++11/C++14 with no modifications in NonCop?

Currently I'm using workaround with deriving from NonCop with wrapper that "deexplicits" the constructor but it doesn't seem very pretty.

like image 972
peper0 Avatar asked Feb 22 '17 12:02

peper0


2 Answers

No, this isn't possible. There is no mechanism to call explicit constructors while returning from a function in C++11 or 14 without having an implicit move or copy (that the compiler will certainly elide).

In C++17 you can just type return NonCop(1,2); and due to "guaranteed elision" it will no longer require a move or copy constructor.


But this is C++, so yes, I can make your code work with zero additional overhead. By cheating, and returning a different type.

template<class T>
struct implicit_construct:T {
  template<class...Ts>
  implicit_construct(Ts&&...ts):
    T(std::forward<Ts>(ts)...) // note: this is an EXPLICIT construction
  {}
};

implicit_construct<NonCop> get_non_cop()
{
  return {1, 2};
}

Live example.

An implicit_construct<NonCop> derives from a NonCop, so you can store the return value in a NonCop&&.


If you are writing NonCop yourself, then what I'd do is add:

 struct explicit_construct_t {};
 // ...
 struct NonCop {
   // ...
   template<class...Ts>
   NonCop( explicit_construct_t, Ts&&...ts ):
     NonCop( std::forward<Ts>(ts)... )
  {}
  // ...
};

which means you can call explicit constructors by prefixing it with a explicit_construct_t to call them implicitly:

NonCop get_non_cop() {
  return {explicit_construct_t{}, 1, 2};
}
like image 70
Yakk - Adam Nevraumont Avatar answered Sep 28 '22 08:09

Yakk - Adam Nevraumont


Unfortunatelly, in this context you can't return the temporary object, since in a return by value expression the compiler requires that the object's copy constructor is accessible, even if this object is going to be copy elided in the end. However, you could return a std::unique_ptr instead of concrete object.

std::unique_ptr<NonCop> get_non_cop() {
    return std::make_unique<NonCop>(1, 2);
}

Live Demo

like image 45
101010 Avatar answered Sep 28 '22 08:09

101010