How to make a function that can return either a static or automatic storage objects without copy in C++?
Suppose I want to write something a function such that:
- it returns an object
- under certain circumstances, depending on a parameter of the function, the object has a fixed value that can be calculated only once to save time. So the natural choice is to make that object
static
. - otherwise, the function must generate the object on the fly
What is the best to write this function, with the requirements that:
- no copy constructors are required upon call, only move, to prevent copying all the data of an expensive object
- I don't want to use
new
raw pointers. If pointers are required, they must be smart and auto delete
The use case is the following:
- certain values are very common, so I want to cache them
- other values are very rare, so I want them not to be cached
The caller should not know which values are common, the interface should be transparent for both cases.
So far, the only clean implementation that I've managed is to use shared_ptr
as shown below, but it feels like overkill. In particular, because it makes a heap allocation, where it feels that one is not really required. Is there a better approach?
#include <cassert>
#include <iostream>
#include <memory>
struct C
int i;
static int count;
C(int i) : i(i)
std::cout << "constr" << std::endl;
count++;
C(const C& c) : C(c.i)
std::cout << "copy" << std::endl;
~C()
std::cout << "destr" << std::endl;
count--;
;
int C::count = 0;
std::shared_ptr<C> func_reg_maybe_static(int i)
static auto static_obj = std::make_shared<C>(0);
if (i == 0)
return static_obj;
else
return std::make_shared<C>(i);
int main()
assert(C::count == 0);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(1));
assert(c->i == 1);
assert(C::count == 2);
assert(C::count == 1);
auto c(func_reg_maybe_static(2));
assert(c->i == 2);
assert(C::count == 2);
assert(C::count == 1);
Which I compile with GCC 6.4.0:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main.out func_ret_maybe_static.cpp
and it produces the expected output (guaranteed by copy elision I believe):
constr
constr
destr
constr
destr
destr
I've added the static reference counter C::count
just to check that objects are actually getting deleted as expected.
If I didn't have the static case, I would just do directly:
C func_reg_maybe_static(int i)
return C(i);
and copy elision / move semantics would make everything efficient.
However, if I try something analogous as in:
C func_reg_maybe_static(int i)
static C c(0);
return c;
then C++ smartly stops just moving C, and starts to copy it, to avoid corrupting the static
.
c++
|
show 3 more comments
Suppose I want to write something a function such that:
- it returns an object
- under certain circumstances, depending on a parameter of the function, the object has a fixed value that can be calculated only once to save time. So the natural choice is to make that object
static
. - otherwise, the function must generate the object on the fly
What is the best to write this function, with the requirements that:
- no copy constructors are required upon call, only move, to prevent copying all the data of an expensive object
- I don't want to use
new
raw pointers. If pointers are required, they must be smart and auto delete
The use case is the following:
- certain values are very common, so I want to cache them
- other values are very rare, so I want them not to be cached
The caller should not know which values are common, the interface should be transparent for both cases.
So far, the only clean implementation that I've managed is to use shared_ptr
as shown below, but it feels like overkill. In particular, because it makes a heap allocation, where it feels that one is not really required. Is there a better approach?
#include <cassert>
#include <iostream>
#include <memory>
struct C
int i;
static int count;
C(int i) : i(i)
std::cout << "constr" << std::endl;
count++;
C(const C& c) : C(c.i)
std::cout << "copy" << std::endl;
~C()
std::cout << "destr" << std::endl;
count--;
;
int C::count = 0;
std::shared_ptr<C> func_reg_maybe_static(int i)
static auto static_obj = std::make_shared<C>(0);
if (i == 0)
return static_obj;
else
return std::make_shared<C>(i);
int main()
assert(C::count == 0);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(1));
assert(c->i == 1);
assert(C::count == 2);
assert(C::count == 1);
auto c(func_reg_maybe_static(2));
assert(c->i == 2);
assert(C::count == 2);
assert(C::count == 1);
Which I compile with GCC 6.4.0:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main.out func_ret_maybe_static.cpp
and it produces the expected output (guaranteed by copy elision I believe):
constr
constr
destr
constr
destr
destr
I've added the static reference counter C::count
just to check that objects are actually getting deleted as expected.
If I didn't have the static case, I would just do directly:
C func_reg_maybe_static(int i)
return C(i);
and copy elision / move semantics would make everything efficient.
However, if I try something analogous as in:
C func_reg_maybe_static(int i)
static C c(0);
return c;
then C++ smartly stops just moving C, and starts to copy it, to avoid corrupting the static
.
c++
1
Not quite clear what else you want - if you return object by value you either have to move or copy it. You can move automatic but must copy static object obviously. Or use smart pointer.
– Slava
Nov 14 '18 at 16:34
@Slava any better way thanshared_ptr
as I've done, or is this a valid use ofshared_ptr
?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 16:36
1
Any use ofstd::shared_ptr
is valid unless you abuse it. I think in this case it is most straightforward and easy to understand solution.
– Slava
Nov 14 '18 at 16:42
@Slava You can return a reference to a static-local, no need forshared_ptr
– Lightness Races in Orbit
Nov 14 '18 at 16:46
1
@LightnessRacesinOrbit you can return a reference to a static, but you cannot return a reference to an automatic, and it seems you need both a static and an automatic here.
– n.m.
Nov 14 '18 at 17:20
|
show 3 more comments
Suppose I want to write something a function such that:
- it returns an object
- under certain circumstances, depending on a parameter of the function, the object has a fixed value that can be calculated only once to save time. So the natural choice is to make that object
static
. - otherwise, the function must generate the object on the fly
What is the best to write this function, with the requirements that:
- no copy constructors are required upon call, only move, to prevent copying all the data of an expensive object
- I don't want to use
new
raw pointers. If pointers are required, they must be smart and auto delete
The use case is the following:
- certain values are very common, so I want to cache them
- other values are very rare, so I want them not to be cached
The caller should not know which values are common, the interface should be transparent for both cases.
So far, the only clean implementation that I've managed is to use shared_ptr
as shown below, but it feels like overkill. In particular, because it makes a heap allocation, where it feels that one is not really required. Is there a better approach?
#include <cassert>
#include <iostream>
#include <memory>
struct C
int i;
static int count;
C(int i) : i(i)
std::cout << "constr" << std::endl;
count++;
C(const C& c) : C(c.i)
std::cout << "copy" << std::endl;
~C()
std::cout << "destr" << std::endl;
count--;
;
int C::count = 0;
std::shared_ptr<C> func_reg_maybe_static(int i)
static auto static_obj = std::make_shared<C>(0);
if (i == 0)
return static_obj;
else
return std::make_shared<C>(i);
int main()
assert(C::count == 0);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(1));
assert(c->i == 1);
assert(C::count == 2);
assert(C::count == 1);
auto c(func_reg_maybe_static(2));
assert(c->i == 2);
assert(C::count == 2);
assert(C::count == 1);
Which I compile with GCC 6.4.0:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main.out func_ret_maybe_static.cpp
and it produces the expected output (guaranteed by copy elision I believe):
constr
constr
destr
constr
destr
destr
I've added the static reference counter C::count
just to check that objects are actually getting deleted as expected.
If I didn't have the static case, I would just do directly:
C func_reg_maybe_static(int i)
return C(i);
and copy elision / move semantics would make everything efficient.
However, if I try something analogous as in:
C func_reg_maybe_static(int i)
static C c(0);
return c;
then C++ smartly stops just moving C, and starts to copy it, to avoid corrupting the static
.
c++
Suppose I want to write something a function such that:
- it returns an object
- under certain circumstances, depending on a parameter of the function, the object has a fixed value that can be calculated only once to save time. So the natural choice is to make that object
static
. - otherwise, the function must generate the object on the fly
What is the best to write this function, with the requirements that:
- no copy constructors are required upon call, only move, to prevent copying all the data of an expensive object
- I don't want to use
new
raw pointers. If pointers are required, they must be smart and auto delete
The use case is the following:
- certain values are very common, so I want to cache them
- other values are very rare, so I want them not to be cached
The caller should not know which values are common, the interface should be transparent for both cases.
So far, the only clean implementation that I've managed is to use shared_ptr
as shown below, but it feels like overkill. In particular, because it makes a heap allocation, where it feels that one is not really required. Is there a better approach?
#include <cassert>
#include <iostream>
#include <memory>
struct C
int i;
static int count;
C(int i) : i(i)
std::cout << "constr" << std::endl;
count++;
C(const C& c) : C(c.i)
std::cout << "copy" << std::endl;
~C()
std::cout << "destr" << std::endl;
count--;
;
int C::count = 0;
std::shared_ptr<C> func_reg_maybe_static(int i)
static auto static_obj = std::make_shared<C>(0);
if (i == 0)
return static_obj;
else
return std::make_shared<C>(i);
int main()
assert(C::count == 0);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(0));
assert(c->i == 0);
assert(C::count == 1);
assert(C::count == 1);
auto c(func_reg_maybe_static(1));
assert(c->i == 1);
assert(C::count == 2);
assert(C::count == 1);
auto c(func_reg_maybe_static(2));
assert(c->i == 2);
assert(C::count == 2);
assert(C::count == 1);
Which I compile with GCC 6.4.0:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main.out func_ret_maybe_static.cpp
and it produces the expected output (guaranteed by copy elision I believe):
constr
constr
destr
constr
destr
destr
I've added the static reference counter C::count
just to check that objects are actually getting deleted as expected.
If I didn't have the static case, I would just do directly:
C func_reg_maybe_static(int i)
return C(i);
and copy elision / move semantics would make everything efficient.
However, if I try something analogous as in:
C func_reg_maybe_static(int i)
static C c(0);
return c;
then C++ smartly stops just moving C, and starts to copy it, to avoid corrupting the static
.
c++
c++
edited Nov 14 '18 at 17:08
Ciro Santilli 新疆改造中心 六四事件 法轮功
asked Nov 14 '18 at 16:24
Ciro Santilli 新疆改造中心 六四事件 法轮功Ciro Santilli 新疆改造中心 六四事件 法轮功
144k33549465
144k33549465
1
Not quite clear what else you want - if you return object by value you either have to move or copy it. You can move automatic but must copy static object obviously. Or use smart pointer.
– Slava
Nov 14 '18 at 16:34
@Slava any better way thanshared_ptr
as I've done, or is this a valid use ofshared_ptr
?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 16:36
1
Any use ofstd::shared_ptr
is valid unless you abuse it. I think in this case it is most straightforward and easy to understand solution.
– Slava
Nov 14 '18 at 16:42
@Slava You can return a reference to a static-local, no need forshared_ptr
– Lightness Races in Orbit
Nov 14 '18 at 16:46
1
@LightnessRacesinOrbit you can return a reference to a static, but you cannot return a reference to an automatic, and it seems you need both a static and an automatic here.
– n.m.
Nov 14 '18 at 17:20
|
show 3 more comments
1
Not quite clear what else you want - if you return object by value you either have to move or copy it. You can move automatic but must copy static object obviously. Or use smart pointer.
– Slava
Nov 14 '18 at 16:34
@Slava any better way thanshared_ptr
as I've done, or is this a valid use ofshared_ptr
?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 16:36
1
Any use ofstd::shared_ptr
is valid unless you abuse it. I think in this case it is most straightforward and easy to understand solution.
– Slava
Nov 14 '18 at 16:42
@Slava You can return a reference to a static-local, no need forshared_ptr
– Lightness Races in Orbit
Nov 14 '18 at 16:46
1
@LightnessRacesinOrbit you can return a reference to a static, but you cannot return a reference to an automatic, and it seems you need both a static and an automatic here.
– n.m.
Nov 14 '18 at 17:20
1
1
Not quite clear what else you want - if you return object by value you either have to move or copy it. You can move automatic but must copy static object obviously. Or use smart pointer.
– Slava
Nov 14 '18 at 16:34
Not quite clear what else you want - if you return object by value you either have to move or copy it. You can move automatic but must copy static object obviously. Or use smart pointer.
– Slava
Nov 14 '18 at 16:34
@Slava any better way than
shared_ptr
as I've done, or is this a valid use of shared_ptr
?– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 16:36
@Slava any better way than
shared_ptr
as I've done, or is this a valid use of shared_ptr
?– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 16:36
1
1
Any use of
std::shared_ptr
is valid unless you abuse it. I think in this case it is most straightforward and easy to understand solution.– Slava
Nov 14 '18 at 16:42
Any use of
std::shared_ptr
is valid unless you abuse it. I think in this case it is most straightforward and easy to understand solution.– Slava
Nov 14 '18 at 16:42
@Slava You can return a reference to a static-local, no need for
shared_ptr
– Lightness Races in Orbit
Nov 14 '18 at 16:46
@Slava You can return a reference to a static-local, no need for
shared_ptr
– Lightness Races in Orbit
Nov 14 '18 at 16:46
1
1
@LightnessRacesinOrbit you can return a reference to a static, but you cannot return a reference to an automatic, and it seems you need both a static and an automatic here.
– n.m.
Nov 14 '18 at 17:20
@LightnessRacesinOrbit you can return a reference to a static, but you cannot return a reference to an automatic, and it seems you need both a static and an automatic here.
– n.m.
Nov 14 '18 at 17:20
|
show 3 more comments
3 Answers
3
active
oldest
votes
I don't really understand the purpose of the static/automatic stuff. If all you're trying to do is avoid repeated constructions when the argument has been used before, why not just have a cache instead?
C& func_reg(const int i)
static std::unordered_map<int, C> cache;
auto it = cache.find(i);
if (it == cache.end())
it = cache.emplace(i, C(i));
return it->second;
Don't always want the cache? Fine! Add this:
C func_reg_nocache(const int i)
return C(i);
If I've misunderstood and you really need this thing where passing is_static == true
gives you an entirely different object (one constructed with 0
rather than i
) then just make a new function for that; it's doing something distinct.
C& func_reg()
static C obj(0);
return obj;
C func_reg(const int i)
return C(i);
1
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
1
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
1
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
|
show 3 more comments
The returned object may refer to a preexisting object depending on an opaque criteria. This implies that the function needs to return by reference. But since the storage for an uncached value must outlive the function and not be on the heap, the caller needs to preemptively provide the storage on the stack for the returned object.
C& func_reg_maybe_static(int i, void* buf)
static C c(0);
if(i == 0)
return c;
else
return *new (buf) C(i);
using uninit_C = std::aligned_storage<sizeof(C), alignof(C)>;
uninit_C buf;
auto& c = func_reg_maybe_static(i, &buf);
This requires a manual destructor call further down the line if and only if the destructor has side effects. Another take would be
C& func_reg_maybe_static(int i, C& buf)
static C c(0);
if(i == 0)
return c;
else
buf.~C();
return *new (&buf) C(i); // note below
C buf;
auto& c = func_reg_maybe_static(i, buf);
This version obviously requires C
be default constructible. The placement new is perfectly legal, but using buf
after the function call may or may not be legal depending on the contents of C
. The destructor will be called on scope exit.
Why do we needreturn *new (buf) C(i);
, and not justreturn C(i)
in the else statement?
– Sahil Dhoked
Nov 14 '18 at 18:55
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
1
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
|
show 3 more comments
What you want is essentially a variant:
std::variant<C, C*> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return &static_obj;
else
return std::variant<C, C*> std::inplace_type_t<C>, i ;
This has the advantage of not requiring any additional memory allocations (compared to the shared_ptr
approach). But it is somewhat inconvenient to use on the caller side. We could work around this by writing a wrapper class:
template <class C> class value_or_ptr
std::variant<C, C*> object_;
public:
explicit template <class... Params> value_or_ptr(Params&&... parameters)
: object_(std::inplace_type_t<C>, std::forward<Params>(parameters)...)
explicit value_or_ptr(C* object)
: object_(object)
// other constructors...
C& operator*()
return object_.index() == 0 ? std::get<0>(object_) : *std::get<1>(object_);
// other accessors...
If you don't have a C++17 compiler, the same can be done with unions, but the implementation will of course be more complex. The original function then becomes:
value_or_ptr<C> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return value_or_ptr &static_obj ;
else
return value_or_ptr i ;
Contrast with Overloads
We could achieve similar results with function overloads:
C& func_reg()
static C obj 0 ;
return obj;
C func_reg(const int i)
return C i ;
This has the advantage of being simpler but might require the caller to make a copy. It also requires the caller to know if a pre-computed object will be returned (which is undesirable). With the variant approach, the caller can treat the results uniformly. The caller always gets back an object with value semantics and does not need to know if he will receive a pre-computed object.
1
How this overcomplicated way is better than usestd::shared_ptr
, which is pretty obvious?
– Slava
Nov 14 '18 at 16:41
2
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
Or just two separate functions, one returningC&
and the other returningC
! I don't see the need forshared_ptr
at all. I must be missing something.
– Lightness Races in Orbit
Nov 14 '18 at 16:43
It can be done much simpler way -std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.
– Slava
Nov 14 '18 at 16:43
1
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
|
show 3 more comments
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53304666%2fhow-to-make-a-function-that-can-return-either-a-static-or-automatic-storage-obje%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
I don't really understand the purpose of the static/automatic stuff. If all you're trying to do is avoid repeated constructions when the argument has been used before, why not just have a cache instead?
C& func_reg(const int i)
static std::unordered_map<int, C> cache;
auto it = cache.find(i);
if (it == cache.end())
it = cache.emplace(i, C(i));
return it->second;
Don't always want the cache? Fine! Add this:
C func_reg_nocache(const int i)
return C(i);
If I've misunderstood and you really need this thing where passing is_static == true
gives you an entirely different object (one constructed with 0
rather than i
) then just make a new function for that; it's doing something distinct.
C& func_reg()
static C obj(0);
return obj;
C func_reg(const int i)
return C(i);
1
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
1
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
1
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
|
show 3 more comments
I don't really understand the purpose of the static/automatic stuff. If all you're trying to do is avoid repeated constructions when the argument has been used before, why not just have a cache instead?
C& func_reg(const int i)
static std::unordered_map<int, C> cache;
auto it = cache.find(i);
if (it == cache.end())
it = cache.emplace(i, C(i));
return it->second;
Don't always want the cache? Fine! Add this:
C func_reg_nocache(const int i)
return C(i);
If I've misunderstood and you really need this thing where passing is_static == true
gives you an entirely different object (one constructed with 0
rather than i
) then just make a new function for that; it's doing something distinct.
C& func_reg()
static C obj(0);
return obj;
C func_reg(const int i)
return C(i);
1
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
1
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
1
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
|
show 3 more comments
I don't really understand the purpose of the static/automatic stuff. If all you're trying to do is avoid repeated constructions when the argument has been used before, why not just have a cache instead?
C& func_reg(const int i)
static std::unordered_map<int, C> cache;
auto it = cache.find(i);
if (it == cache.end())
it = cache.emplace(i, C(i));
return it->second;
Don't always want the cache? Fine! Add this:
C func_reg_nocache(const int i)
return C(i);
If I've misunderstood and you really need this thing where passing is_static == true
gives you an entirely different object (one constructed with 0
rather than i
) then just make a new function for that; it's doing something distinct.
C& func_reg()
static C obj(0);
return obj;
C func_reg(const int i)
return C(i);
I don't really understand the purpose of the static/automatic stuff. If all you're trying to do is avoid repeated constructions when the argument has been used before, why not just have a cache instead?
C& func_reg(const int i)
static std::unordered_map<int, C> cache;
auto it = cache.find(i);
if (it == cache.end())
it = cache.emplace(i, C(i));
return it->second;
Don't always want the cache? Fine! Add this:
C func_reg_nocache(const int i)
return C(i);
If I've misunderstood and you really need this thing where passing is_static == true
gives you an entirely different object (one constructed with 0
rather than i
) then just make a new function for that; it's doing something distinct.
C& func_reg()
static C obj(0);
return obj;
C func_reg(const int i)
return C(i);
edited Nov 14 '18 at 16:46
answered Nov 14 '18 at 16:34
Lightness Races in OrbitLightness Races in Orbit
292k52475808
292k52475808
1
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
1
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
1
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
|
show 3 more comments
1
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
1
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
1
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
1
1
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
There's nothing left with automatic storage then, right?
– YSC
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
@YSC: Nope but I'm wilfully dropping that requirement because I don't really understand its value. Speaking functionally, that is. OP does allow for dynamic allocation as long as things are cleaned up automatically.
– Lightness Races in Orbit
Nov 14 '18 at 16:35
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
From OP's last comment, I fully agree. +1.
– YSC
Nov 14 '18 at 16:36
1
1
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
@CiroSantilli新疆改造中心六四事件法轮功 Maybe but now we're really in the realm of me not understanding what problem you're trying to solve, sorry.
– Lightness Races in Orbit
Nov 14 '18 at 16:39
1
1
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
You made me realize that I had not mapped my problem well to the question. I have edited the question to reflect that, hopefully this is the right approach in this case rather than asking a new one.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:09
|
show 3 more comments
The returned object may refer to a preexisting object depending on an opaque criteria. This implies that the function needs to return by reference. But since the storage for an uncached value must outlive the function and not be on the heap, the caller needs to preemptively provide the storage on the stack for the returned object.
C& func_reg_maybe_static(int i, void* buf)
static C c(0);
if(i == 0)
return c;
else
return *new (buf) C(i);
using uninit_C = std::aligned_storage<sizeof(C), alignof(C)>;
uninit_C buf;
auto& c = func_reg_maybe_static(i, &buf);
This requires a manual destructor call further down the line if and only if the destructor has side effects. Another take would be
C& func_reg_maybe_static(int i, C& buf)
static C c(0);
if(i == 0)
return c;
else
buf.~C();
return *new (&buf) C(i); // note below
C buf;
auto& c = func_reg_maybe_static(i, buf);
This version obviously requires C
be default constructible. The placement new is perfectly legal, but using buf
after the function call may or may not be legal depending on the contents of C
. The destructor will be called on scope exit.
Why do we needreturn *new (buf) C(i);
, and not justreturn C(i)
in the else statement?
– Sahil Dhoked
Nov 14 '18 at 18:55
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
1
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
|
show 3 more comments
The returned object may refer to a preexisting object depending on an opaque criteria. This implies that the function needs to return by reference. But since the storage for an uncached value must outlive the function and not be on the heap, the caller needs to preemptively provide the storage on the stack for the returned object.
C& func_reg_maybe_static(int i, void* buf)
static C c(0);
if(i == 0)
return c;
else
return *new (buf) C(i);
using uninit_C = std::aligned_storage<sizeof(C), alignof(C)>;
uninit_C buf;
auto& c = func_reg_maybe_static(i, &buf);
This requires a manual destructor call further down the line if and only if the destructor has side effects. Another take would be
C& func_reg_maybe_static(int i, C& buf)
static C c(0);
if(i == 0)
return c;
else
buf.~C();
return *new (&buf) C(i); // note below
C buf;
auto& c = func_reg_maybe_static(i, buf);
This version obviously requires C
be default constructible. The placement new is perfectly legal, but using buf
after the function call may or may not be legal depending on the contents of C
. The destructor will be called on scope exit.
Why do we needreturn *new (buf) C(i);
, and not justreturn C(i)
in the else statement?
– Sahil Dhoked
Nov 14 '18 at 18:55
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
1
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
|
show 3 more comments
The returned object may refer to a preexisting object depending on an opaque criteria. This implies that the function needs to return by reference. But since the storage for an uncached value must outlive the function and not be on the heap, the caller needs to preemptively provide the storage on the stack for the returned object.
C& func_reg_maybe_static(int i, void* buf)
static C c(0);
if(i == 0)
return c;
else
return *new (buf) C(i);
using uninit_C = std::aligned_storage<sizeof(C), alignof(C)>;
uninit_C buf;
auto& c = func_reg_maybe_static(i, &buf);
This requires a manual destructor call further down the line if and only if the destructor has side effects. Another take would be
C& func_reg_maybe_static(int i, C& buf)
static C c(0);
if(i == 0)
return c;
else
buf.~C();
return *new (&buf) C(i); // note below
C buf;
auto& c = func_reg_maybe_static(i, buf);
This version obviously requires C
be default constructible. The placement new is perfectly legal, but using buf
after the function call may or may not be legal depending on the contents of C
. The destructor will be called on scope exit.
The returned object may refer to a preexisting object depending on an opaque criteria. This implies that the function needs to return by reference. But since the storage for an uncached value must outlive the function and not be on the heap, the caller needs to preemptively provide the storage on the stack for the returned object.
C& func_reg_maybe_static(int i, void* buf)
static C c(0);
if(i == 0)
return c;
else
return *new (buf) C(i);
using uninit_C = std::aligned_storage<sizeof(C), alignof(C)>;
uninit_C buf;
auto& c = func_reg_maybe_static(i, &buf);
This requires a manual destructor call further down the line if and only if the destructor has side effects. Another take would be
C& func_reg_maybe_static(int i, C& buf)
static C c(0);
if(i == 0)
return c;
else
buf.~C();
return *new (&buf) C(i); // note below
C buf;
auto& c = func_reg_maybe_static(i, buf);
This version obviously requires C
be default constructible. The placement new is perfectly legal, but using buf
after the function call may or may not be legal depending on the contents of C
. The destructor will be called on scope exit.
edited Nov 15 '18 at 7:19
answered Nov 14 '18 at 18:33
Passer ByPasser By
9,92432558
9,92432558
Why do we needreturn *new (buf) C(i);
, and not justreturn C(i)
in the else statement?
– Sahil Dhoked
Nov 14 '18 at 18:55
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
1
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
|
show 3 more comments
Why do we needreturn *new (buf) C(i);
, and not justreturn C(i)
in the else statement?
– Sahil Dhoked
Nov 14 '18 at 18:55
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
1
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
Why do we need
return *new (buf) C(i);
, and not just return C(i)
in the else statement?– Sahil Dhoked
Nov 14 '18 at 18:55
Why do we need
return *new (buf) C(i);
, and not just return C(i)
in the else statement?– Sahil Dhoked
Nov 14 '18 at 18:55
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
@SahilDhoked We return by reference.
– Passer By
Nov 14 '18 at 18:56
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
Cool, I didn't know about placement new. Can you clarify "The placement new is perfectly legal, but using buf after the function call may or may not be legal depending on the contents of C.": when is it now legal?
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 15 '18 at 4:59
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
@CiroSantilli新疆改造中心六四事件法轮功 Link added.
– Passer By
Nov 15 '18 at 7:12
1
1
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
@CiroSantilli新疆改造中心六四事件法轮功 And I'm sorry about the billion edits, I wasn't all that awake when I wrote this.
– Passer By
Nov 15 '18 at 7:21
|
show 3 more comments
What you want is essentially a variant:
std::variant<C, C*> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return &static_obj;
else
return std::variant<C, C*> std::inplace_type_t<C>, i ;
This has the advantage of not requiring any additional memory allocations (compared to the shared_ptr
approach). But it is somewhat inconvenient to use on the caller side. We could work around this by writing a wrapper class:
template <class C> class value_or_ptr
std::variant<C, C*> object_;
public:
explicit template <class... Params> value_or_ptr(Params&&... parameters)
: object_(std::inplace_type_t<C>, std::forward<Params>(parameters)...)
explicit value_or_ptr(C* object)
: object_(object)
// other constructors...
C& operator*()
return object_.index() == 0 ? std::get<0>(object_) : *std::get<1>(object_);
// other accessors...
If you don't have a C++17 compiler, the same can be done with unions, but the implementation will of course be more complex. The original function then becomes:
value_or_ptr<C> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return value_or_ptr &static_obj ;
else
return value_or_ptr i ;
Contrast with Overloads
We could achieve similar results with function overloads:
C& func_reg()
static C obj 0 ;
return obj;
C func_reg(const int i)
return C i ;
This has the advantage of being simpler but might require the caller to make a copy. It also requires the caller to know if a pre-computed object will be returned (which is undesirable). With the variant approach, the caller can treat the results uniformly. The caller always gets back an object with value semantics and does not need to know if he will receive a pre-computed object.
1
How this overcomplicated way is better than usestd::shared_ptr
, which is pretty obvious?
– Slava
Nov 14 '18 at 16:41
2
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
Or just two separate functions, one returningC&
and the other returningC
! I don't see the need forshared_ptr
at all. I must be missing something.
– Lightness Races in Orbit
Nov 14 '18 at 16:43
It can be done much simpler way -std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.
– Slava
Nov 14 '18 at 16:43
1
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
|
show 3 more comments
What you want is essentially a variant:
std::variant<C, C*> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return &static_obj;
else
return std::variant<C, C*> std::inplace_type_t<C>, i ;
This has the advantage of not requiring any additional memory allocations (compared to the shared_ptr
approach). But it is somewhat inconvenient to use on the caller side. We could work around this by writing a wrapper class:
template <class C> class value_or_ptr
std::variant<C, C*> object_;
public:
explicit template <class... Params> value_or_ptr(Params&&... parameters)
: object_(std::inplace_type_t<C>, std::forward<Params>(parameters)...)
explicit value_or_ptr(C* object)
: object_(object)
// other constructors...
C& operator*()
return object_.index() == 0 ? std::get<0>(object_) : *std::get<1>(object_);
// other accessors...
If you don't have a C++17 compiler, the same can be done with unions, but the implementation will of course be more complex. The original function then becomes:
value_or_ptr<C> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return value_or_ptr &static_obj ;
else
return value_or_ptr i ;
Contrast with Overloads
We could achieve similar results with function overloads:
C& func_reg()
static C obj 0 ;
return obj;
C func_reg(const int i)
return C i ;
This has the advantage of being simpler but might require the caller to make a copy. It also requires the caller to know if a pre-computed object will be returned (which is undesirable). With the variant approach, the caller can treat the results uniformly. The caller always gets back an object with value semantics and does not need to know if he will receive a pre-computed object.
1
How this overcomplicated way is better than usestd::shared_ptr
, which is pretty obvious?
– Slava
Nov 14 '18 at 16:41
2
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
Or just two separate functions, one returningC&
and the other returningC
! I don't see the need forshared_ptr
at all. I must be missing something.
– Lightness Races in Orbit
Nov 14 '18 at 16:43
It can be done much simpler way -std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.
– Slava
Nov 14 '18 at 16:43
1
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
|
show 3 more comments
What you want is essentially a variant:
std::variant<C, C*> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return &static_obj;
else
return std::variant<C, C*> std::inplace_type_t<C>, i ;
This has the advantage of not requiring any additional memory allocations (compared to the shared_ptr
approach). But it is somewhat inconvenient to use on the caller side. We could work around this by writing a wrapper class:
template <class C> class value_or_ptr
std::variant<C, C*> object_;
public:
explicit template <class... Params> value_or_ptr(Params&&... parameters)
: object_(std::inplace_type_t<C>, std::forward<Params>(parameters)...)
explicit value_or_ptr(C* object)
: object_(object)
// other constructors...
C& operator*()
return object_.index() == 0 ? std::get<0>(object_) : *std::get<1>(object_);
// other accessors...
If you don't have a C++17 compiler, the same can be done with unions, but the implementation will of course be more complex. The original function then becomes:
value_or_ptr<C> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return value_or_ptr &static_obj ;
else
return value_or_ptr i ;
Contrast with Overloads
We could achieve similar results with function overloads:
C& func_reg()
static C obj 0 ;
return obj;
C func_reg(const int i)
return C i ;
This has the advantage of being simpler but might require the caller to make a copy. It also requires the caller to know if a pre-computed object will be returned (which is undesirable). With the variant approach, the caller can treat the results uniformly. The caller always gets back an object with value semantics and does not need to know if he will receive a pre-computed object.
What you want is essentially a variant:
std::variant<C, C*> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return &static_obj;
else
return std::variant<C, C*> std::inplace_type_t<C>, i ;
This has the advantage of not requiring any additional memory allocations (compared to the shared_ptr
approach). But it is somewhat inconvenient to use on the caller side. We could work around this by writing a wrapper class:
template <class C> class value_or_ptr
std::variant<C, C*> object_;
public:
explicit template <class... Params> value_or_ptr(Params&&... parameters)
: object_(std::inplace_type_t<C>, std::forward<Params>(parameters)...)
explicit value_or_ptr(C* object)
: object_(object)
// other constructors...
C& operator*()
return object_.index() == 0 ? std::get<0>(object_) : *std::get<1>(object_);
// other accessors...
If you don't have a C++17 compiler, the same can be done with unions, but the implementation will of course be more complex. The original function then becomes:
value_or_ptr<C> func_reg_maybe_static(int i)
static C static_obj 0 ;
if (i == 0)
return value_or_ptr &static_obj ;
else
return value_or_ptr i ;
Contrast with Overloads
We could achieve similar results with function overloads:
C& func_reg()
static C obj 0 ;
return obj;
C func_reg(const int i)
return C i ;
This has the advantage of being simpler but might require the caller to make a copy. It also requires the caller to know if a pre-computed object will be returned (which is undesirable). With the variant approach, the caller can treat the results uniformly. The caller always gets back an object with value semantics and does not need to know if he will receive a pre-computed object.
edited Nov 14 '18 at 21:31
answered Nov 14 '18 at 16:39
Peter RudermanPeter Ruderman
10.2k2352
10.2k2352
1
How this overcomplicated way is better than usestd::shared_ptr
, which is pretty obvious?
– Slava
Nov 14 '18 at 16:41
2
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
Or just two separate functions, one returningC&
and the other returningC
! I don't see the need forshared_ptr
at all. I must be missing something.
– Lightness Races in Orbit
Nov 14 '18 at 16:43
It can be done much simpler way -std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.
– Slava
Nov 14 '18 at 16:43
1
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
|
show 3 more comments
1
How this overcomplicated way is better than usestd::shared_ptr
, which is pretty obvious?
– Slava
Nov 14 '18 at 16:41
2
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
Or just two separate functions, one returningC&
and the other returningC
! I don't see the need forshared_ptr
at all. I must be missing something.
– Lightness Races in Orbit
Nov 14 '18 at 16:43
It can be done much simpler way -std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.
– Slava
Nov 14 '18 at 16:43
1
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
1
1
How this overcomplicated way is better than use
std::shared_ptr
, which is pretty obvious?– Slava
Nov 14 '18 at 16:41
How this overcomplicated way is better than use
std::shared_ptr
, which is pretty obvious?– Slava
Nov 14 '18 at 16:41
2
2
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
This avoids unnecessary heap allocations.
– Peter Ruderman
Nov 14 '18 at 16:41
Or just two separate functions, one returning
C&
and the other returning C
! I don't see the need for shared_ptr
at all. I must be missing something.– Lightness Races in Orbit
Nov 14 '18 at 16:43
Or just two separate functions, one returning
C&
and the other returning C
! I don't see the need for shared_ptr
at all. I must be missing something.– Lightness Races in Orbit
Nov 14 '18 at 16:43
It can be done much simpler way -
std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.– Slava
Nov 14 '18 at 16:43
It can be done much simpler way -
std::shared_ptr
pointing to static object if that is really necessary. I doubt it is.– Slava
Nov 14 '18 at 16:43
1
1
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
I have updated the question to prevent cheating with different interfaces :-) Sorry for the mess.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 17:11
|
show 3 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53304666%2fhow-to-make-a-function-that-can-return-either-a-static-or-automatic-storage-obje%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Not quite clear what else you want - if you return object by value you either have to move or copy it. You can move automatic but must copy static object obviously. Or use smart pointer.
– Slava
Nov 14 '18 at 16:34
@Slava any better way than
shared_ptr
as I've done, or is this a valid use ofshared_ptr
?– Ciro Santilli 新疆改造中心 六四事件 法轮功
Nov 14 '18 at 16:36
1
Any use of
std::shared_ptr
is valid unless you abuse it. I think in this case it is most straightforward and easy to understand solution.– Slava
Nov 14 '18 at 16:42
@Slava You can return a reference to a static-local, no need for
shared_ptr
– Lightness Races in Orbit
Nov 14 '18 at 16:46
1
@LightnessRacesinOrbit you can return a reference to a static, but you cannot return a reference to an automatic, and it seems you need both a static and an automatic here.
– n.m.
Nov 14 '18 at 17:20