4

I have the following the code:

struct A {//<-- Third party library class
  A(){};
  A(const A&) = delete;
  void init(){};
  int i;
};


int f(){
  static A a = []{
    A res; 
    res.init(); 
    return res;
    }();
  return a.i;
}

see it live: http://coliru.stacked-crooked.com/a/a5c5912bd79053c3

And it gives the following error when compiled:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

main.cpp: In lambda function:

main.cpp:12:12: error: use of deleted function 'A::A(const A&)'

     return res;

            ^~~

main.cpp:4:3: note: declared here

   A(const A&) = delete;

   ^

I know I could wrap this in a another struct and initialize in that constructor, but it just seems somewhat tedious. Using , do we have a 'neat' way around this 'problem' ?

Any solution must work equally well for a static var in a function

darune
  • 10,480
  • 2
  • 24
  • 62

2 Answers2

4

If your class is not moveable, and the copy constructor is deleted/not accessible, then you don't have any choice but to return a reference to a static object, or to return a pointer. This is because C++17's guaranteed copy elision only works with prvalues. Since the object has a name in the lambda, it has to be able to be moved or copied it into the return object, even if NRVO is applied.

You can use

A& a = []() -> A& {
    static A res; 
    res.init(); 
    return res;
}();

or

auto a = []{
    auto res = std::make_unique<A>(); 
    res->init(); 
    return res;
}();
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • This is somewhat what i gathered as well. Keeping the question open for a bit. – darune Mar 29 '19 at 12:57
  • @NathanOliver which of the two possible approaches would you personally prefer? – Stack Danny Mar 29 '19 at 12:59
  • @darune No worries. It's unfortunate that MS didn't make the type moveable. Maybe they will update it one day. – NathanOliver Mar 29 '19 at 12:59
  • 1
    @StackDanny Tough choice. I'm leaning towards the `unique_ptr` as it naturally translates into a factory function approach for building multiple `A`'s. The static approach doesn't work for that since they would all be sharing a single instance. This is doubly so if multi-threading is in play. – NathanOliver Mar 29 '19 at 13:02
-1

You can call init on default constructor. Then, you can have this on static functions and static member functions. It is also possible to use copy elision on prvalues and provide something like A res = []{ return A(); }();.

#include <iostream>

struct A {//Third party library class
  constexpr A() :i{}
  { init(); };
  A(const A&) = delete;
  constexpr void init(){ i = 5; };
  int i;
};

struct A_User {
    static constexpr void A_print() {
        constexpr A res{};
        static_assert(res.i == 5);
    }
};

A& static_func() {
    static A res{};
    std::cout << "res static: " << res.i << "\n";
    return res;
}

A res = []{ return A(); /* copy elided since this is prvalue */ }();

int main() {
    std::cout << "res global: " << res.i << "\n";
    auto& static_A = static_func();
} 
meguli
  • 1,426
  • 1
  • 18
  • 35
  • this is too far away from problem or only half of an answer - it will only work for global (and my actual case is a static var in function) and the other issue is that it could be needed before entering main function – darune Mar 29 '19 at 12:32
  • We can do `A res = []{ return A(); }();` for globals but as others pointed, seems like there is no way to return a `static A` by value from a function. – meguli Mar 29 '19 at 13:51