I am looking to do something like this (C#).
public final class ImmutableClass {
public readonly int i;
public readonly OtherImmutableClass o;
public readonly ReadOnlyCollection<OtherImmutableClass> r;
public ImmutableClass(int i, OtherImmutableClass o,
ReadOnlyCollection<OtherImmutableClass> r) : i(i), o(o), r(r) {}
}
The potential solutions and their associated problems I've encountered are:
1. Using const for the class members, but this means the default copy assignment operator is deleted.
Solution 1:
struct OtherImmutableObject {
const int i1;
const int i2;
OtherImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
}
Problem 1:
OtherImmutableObject o1(1,2);
OtherImmutableObject o2(2,3);
o1 = o2; // error: use of deleted function 'OtherImmutableObject& OtherImmutableObject::operator=(const OtherImmutableObject&)`
EDIT: This is important as I would like to store immutable objects in a std::vector but receive error: use of deleted function 'OtherImmutableObject& OtherImmutableObject::operator=(OtherImmutableObject&&)
2. Using get methods and returning values, but this means that large objects would have to be copied which is an inefficiency I'd like to know how to avoid. This thread suggests the get solution, but it doesn't address how to handle passing non-primitive objects without copying the original object.
Solution 2:
class OtherImmutableObject {
int i1;
int i2;
public:
OtherImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
int GetI1() { return i1; }
int GetI2() { return i2; }
}
class ImmutableObject {
int i1;
OtherImmutableObject o;
std::vector<OtherImmutableObject> v;
public:
ImmutableObject(int i1, OtherImmutableObject o,
std::vector<OtherImmutableObject> v) : i1(i1), o(o), v(v) {}
int GetI1() { return i1; }
OtherImmutableObject GetO() { return o; } // Copies a value that should be immutable and therefore able to be safely used elsewhere.
std::vector<OtherImmutableObject> GetV() { return v; } // Copies the vector.
}
Problem 2: The unnecessary copies are inefficient.
3. Using get methods and returning const references or const pointers but this could leave hanging references or pointers. This thread talks about the dangers of references going out of scope from function returns.
Solution 3:
class OtherImmutableObject {
int i1;
int i2;
public:
OtherImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
int GetI1() { return i1; }
int GetI2() { return i2; }
}
class ImmutableObject {
int i1;
OtherImmutableObject o;
std::vector<OtherImmutableObject> v;
public:
ImmutableObject(int i1, OtherImmutableObject o,
std::vector<OtherImmutableObject> v) : i1(i1), o(o), v(v) {}
int GetI1() { return i1; }
const OtherImmutableObject& GetO() { return o; }
const std::vector<OtherImmutableObject>& GetV() { return v; }
}
Problem 3:
ImmutableObject immutable_object(1,o,v);
// elsewhere in code...
OtherImmutableObject& other_immutable_object = immutable_object.GetO();
// Somewhere else immutable_object goes out of scope, but not other_immutable_object
// ...and then...
other_immutable_object.GetI1();
// The previous line is undefined behaviour as immutable_object.o will have been deleted with immutable_object going out of scope
Undefined behaviour can occur due to returning a reference from any of the Get methods.
You truly want immutable objects of some type plus value semantics (as you care about runtime performance and want to avoid the heap). Just define a struct with all data members public.
struct Immutable {
const std::string str;
const int i;
};
You can instantiate and copy them, read data members, but that's about it. Move-constructing an instance from an rvalue reference of another one still copies.
Immutable obj1{"...", 42};
Immutable obj2 = obj1;
Immutable obj3 = std::move(obj1); // Copies, too
obj3 = obj2; // Error, cannot assign
This way, you really make sure every usage of your class respects the immutability (assuming no one does bad const_cast things). Additional functionality can be provided through free functions, there is no point in adding member functions to a read-only aggregation of data members.
You want 1., still with value semantics, but slightly relaxed (such that the objects aren't really immutable anymore) and you're also concerned that you need move-construction for the sake of runtime performance. There is no way around private data members and getter member functions:
class Immutable {
public:
Immutable(std::string str, int i) : str{std::move(str)}, i{i} {}
const std::string& getStr() const { return str; }
int getI() const { return i; }
private:
std::string str;
int i;
};
Usage is the same, but the move construction really does move.
Immutable obj1{"...", 42};
Immutable obj2 = obj1;
Immutable obj3 = std::move(obj1); // Ok, does move-construct members
Whether you want assignment to be allowed or not is under your control now. Just = delete the assignment operators if you don't want it, otherwise go with the compiler-generated one or implement your own.
obj3 = obj2; // Ok if not manually disabled
You don't care about value semantics and/or atomic reference count increments are ok in your scenario. Use the solution depicted in #NathanOliver's answer.
You can basically get what you want by leveraging a std::unique_ptr or std::shared_ptr. If you only want one of these objects, but allow for it to be moved around, then you can use a std::unique_ptr. If you want to allow for multiple objects ("copies") that all have the same value, then you can use a std::shared_Ptr. Use an alias to shorten the name and provide a factory function and it becomes pretty painless. That would make your code look like:
class ImmutableClassImpl {
public:
const int i;
const OtherImmutableClass o;
const ReadOnlyCollection<OtherImmutableClass> r;
public ImmutableClassImpl(int i, OtherImmutableClass o,
ReadOnlyCollection<OtherImmutableClass> r) : i(i), o(o), r(r) {}
}
using Immutable = std::unique_ptr<ImmutableClassImpl>;
template<typename... Args>
Immutable make_immutable(Args&&... args)
{
return std::make_unique<ImmutableClassImpl>(std::forward<Args>(args)...);
}
int main()
{
auto first = make_immutable(...);
// first points to a unique object now
// can be accessed like
std::cout << first->i;
auto second = make_immutable(...);
// now we have another object that is separate from first
// we can't do
// second = first;
// but we can transfer like
second = std::move(first);
// which leaves first in an empty state where you can give it a new object to point to
}
If the code is changes to use a shared_ptr instead then you could do
second = first;
and then both objects point to the same object, but neither can modify it.
Immutability in C++ can't be directly compared to immutability in most other popular languages because of C++'s universal value semantics. You have to figure out what you want "immutable" to mean.
You want to be able to assign new values to variables of type OtherImmutableObject. That makes sense, since you can do that with variables of type ImmutableObject in C#.
In that case, the simplest way to get the semantics you want is
struct OtherImmutableObject {
int i1;
int i2;
};
It may look like this is mutable. After all, you can write
OtherImmutableObject x{1, 2};
x.i1 = 3;
But the effect of that second line is (ignoring concurrency...) exactly the same as the effect of
x = OtherImmutableObject{3, x.i2};
so if you want to allow assignment to variables of type OtherImmutableObject then it makes no sense to disallow direct assignment to members, since it doesn't provide any additional semantic guarantee; all it does is make the code for the same abstract operation slower. (In this case, most optimizing compilers will probably generate the same code for both expressions, but if one of the members was a std::string they might not be smart enough to do that.)
Note that this is the behavior of basically every standard type in C++, including int, std::complex, std::string, etc. They are all mutable in the sense that you can assign new values to them, and all immutable in the sense that the only thing you can do (abstractly) to change them is assign new values to them, much like immutable reference types in C#.
If you don't want that semantics, your only other option is to forbid assignment. I would advise doing that by declaring your variables to be const, not by declaring all the members of the type to be const, because it gives you more options for how you can use the class. For example, you can create an initially mutable instance of the class, build a value in it, then "freeze" it by using only const references to it thereafter – like converting a StringBuilder to a string, but without the overhead of copying it.
(One possible reason to declare all members to be const might be that it allows for better optimization in some cases. For example, if a function gets an OtherImmutableObject const&, and the compiler can't see the call site, it isn't safe to cache the values of members across calls to other unknown code, since the underlying object may not have the const qualifier. But if the actual members are declared const, then I think it would be safe to cache the values.)
To answer your question, you don't create immutable data structures in C++ because consting references to the whole object does the trick. Violations of the rule are made visible by the presence of const_casts.
If I may refer to Kevlin Henney's "Thinking outside the synchronization quadrant", there are two questions to ask about data:
Is a structure immutable or mutable?
Is it shared or unshared?
These questions can be arranged into a nice 2x2 table with 4 quadrants. In a concurrent context, only one quadrant needs synchronization: shared mutable data.
Indeed, immutable data need not be synchronized because you cannot write to it, and concurrent reads are fine. Unshared data needs not be synchronized, because only the owner of the data can write to it or read from it.
So it is fine for a data structure to be mutable in an unshared context, and the benefits of immutability occur only in a shared context.
IMO, the solution that gives you most freedom is to define your class for both mutability and immutability, using constness only where it makes sense (data that is initalized then never changed):
/* const-correct */ class C {
int f1_;
int f2_;
const int f3_; // Semantic constness : initialized and never changed.
};
You can then use instances of your class C either as mutable or immutable, benefitting of constness-where-it-makes-sense in either case.
If now you want to share your object, you can pack it in a smart pointer to const:
shared_ptr<const C> ptr = make_shared<const C>(f1, f2, f3);
Using this strategy, your freedom spans the whole 3 unsynchronized quandrants while staying safely out of the synchronization quadrant. (therefore, limiting the need of making your structure immutable)
I'd say the most idiomatic way would be that:
struct OtherImmutable {
int i1;
int i2;
OtherImmutable(int i1, int i2) : i1(i1), i2(i2) {}
};
But... that not immutable??
Indeed but you can pass it around as a value:
void frob1() {
OtherImmutable oi;
oi = frob2(oi);
}
auto frob2(OtherImmutable oi) -> OtherImmutable {
// cannot affect frob1 oi, since it's a copy
}
Even better, places that don't need to mutate locally can define its local variables as const:
auto frob2(OtherImmutable const oi) -> OtherImmutable {
return OtherImmutable{oi.i1 + 1, oi.i2};
}
Immutable objects work much better with pointer semantics. So write a smart immutable pointer:
struct immu_tag_t {};
template<class T>
struct immu:std::shared_ptr<T const>
{
using base = std::shared_ptr<T const>;
immu():base( std::make_shared<T const>() ) {}
template<class A0, class...Args,
std::enable_if_t< !std::is_base_of< immu_tag_t, std::decay_t<A0> >{}, bool > = true,
std::enable_if_t< std::is_construtible< T const, A0&&, Args&&... >{}, bool > = true
>
immu(A0&& a0, Args&&...args):
base(
std::make_shared<T const>(
std::forward<A0>(a0), std::forward<Args>(args)...
)
)
{}
template<class A0, class...Args,
std::enable_if_t< std::is_construtible< T const, std::initializer_list<A0>, Args&&... >{}, bool > = true
>
immu(std::initializer_list<A0> a0, Args&&...args):
base(
std::make_shared<T const>(
a0, std::forward<Args>(args)...
)
)
{}
immu( immu_tag_t, std::shared_ptr<T const> ptr ):base(std::move(ptr)) {}
immu(immu&&)=default;
immu(immu const&)=default;
immu& operator=(immu&&)=default;
immu& operator=(immu const&)=default;
template<class F>
immu modify( F&& f ) const {
std::shared_ptr<T> ptr;
if (!*this) {
ptr = std::make_shared<T>();
} else {
ptr = std::make_shared<T>(**this);
}
std::forward<F>(f)(*ptr);
return {immu_tag_t{}, std::move(ptr)};
}
};
This leverages shared_ptr for most of its implementation; most of the disadvantages of shared_ptr are not a problem with immutable objects.
Unlike shared ptr, it permits you to create the object directly, and by default creates a non-null state. It can still reach a null state by being moved-from. You can create one in a null state by doing:
immu<int> immu_null_int{ immu_tag_t{}, {} };
and a non-null int via:
immu<int> immu_int;
or
immu<int> immu_int = 7;
I added a useful utility method called modify. Modify gives you a mutable instance of the T to pass to a lambda to modify before it is returned packaged up in an immu<T>.
Concrete use looks like:
struct data;
using immu_data = immu<data>;
struct data {
int i;
other_immutable_class o;
std::vector<other_immutable_class> r;
data( int i_in, other_immutable_class o_in, std::vector<other_immutable_class> r_in ):
i(i_in), o(std::move(o_in)), r( std::move(r_in))
{}
};
Then use immu_data.
Accessing members requires -> not ., and you should check for null immu_datas if you are passed them.
Here is how you use .modify:
immu_data a( 7, other_immutable_class{}, {} );
immu_data b = a.modify([&](auto& b){ ++b.i; b.r.emplace_back() });
This creates a b whose value is equal to a, except i is incremented by 1, and there is an extra other_immutable_class in b.r (default constructed). Note that a is unmodified by creating b.
There are probably typos above, but I've used the design.
If you want to get fancy, you can make immu support copy-on-write, or modify-in-place if unique. It is harder than it sounds though.
C++ doesn't quite have the ability to predefine a class as immutable or const.
And at some point you'll probably come to the conclusion that you shouldn't use const for class members in C++. It's just not worth the annoyances, and honestly you can do without it.
As a practical solution, I would try:
typedef class _some_SUPER_obtuse_CLASS_NAME_PLEASE_DONT_USE_THIS { } const Immutable;
to discourage anyone from using anything but Immutable in their code.
The issue at hand is a mistranslation from C# to C++. In C++ there is simply no* need to do this:
class ImmutableObject {
ImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
const int i1;
const int i2;
}
ImmutableObject o1(1,2):
ImmutableObject o2(2,3);
o1 = o2; // Doesn't compile, because immutable objects are by definition not mutable.
In your C# example you are using a class. And a variable that holds an instance of a class in C# is really just a reference to a garbage collected object. The closest equivalent in C++ is a reference counted smart pointer. So your c# example is translated to C++ as:
class ImmutableObject {
ImmutableObject(int i1, int i2) : i1(i1), i2(i2) {}
const int i1;
const int i2;
}
std::shared_ptr<ImmutableObject> o1 = std::make_shared<ImmutableObject>(1,2);
std::shared_ptr<ImmutableObject> o2 = std::make_shared<ImmutableObject>(2,3);
o1 = o2; // Does compile because shared_ptr is mutable.
There are several options if you want a mutable reference to an immutable/const object, specifically you can use a pointer, a smart pointer, or a reference_wrapper. Unless you actually want to have a class whose content can be changed by anyone at any time, which is the opposite of an immutable class.
*Of course, C++ is a language where "no" doesn't exist. In those precious few truly exceptional circumstances you can use const_cast.
Related
In the library I am designing, I sometimes need read-access to large member variables of classes. Because of their size I don't want to make a getter that returns by copying the member. I don't want them to be modifiable from the outside, so I can't have them public or return references to them. So I thought I would use a "reader":
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
const double& readD() const { return d_; }
private:
double d_;
};
(this is not really meant for doubles)
But here somebody could const_cast the reference and access the data directly. Even without assuming malicious intent, somebody could safe a reference to the data-member and keep it around after the original object has gone out of scope. I know const references can keep a temporary viable, but that doesn't remove the const_cast-problem.
So I came up with a workaround:
#include <iostream>
template<class T>
class SafeMemberReference
{
public:
using type = T;
SafeMemberReference(const T& t) :t(t) {}
explicit SafeMemberReference(T&& t) = delete;
operator const T& () && {return t; }
T get() && {return t; }
private:
const T& t;
};
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
SafeMemberReference<double> readD() const { return d_; }
private:
double d_;
};
int main()
{
TestClass foo(1.2);
// temporary from read can be used as temporary in expressions
std::cout << foo.readD() << std::endl;
// temporary can be used to copy from
auto x = foo.readD().get();
// lvalue can not be used, so a possible dangling reference is no problem
auto ref = foo.readD();
//std::cout << ref << std::endl;
}
I have several questions to this:
Q1) How necessary is this from an efficiency POV? the largest objects I am returning are dense complex matrices with dimensions of maybe 1000x1000. These copies may happen frequently
Q2) Are my concerns about returning by const& valid?
Q3) Does this seem like a good solution? which drawbacks does it have?
Any solution that attempts to fight the language itself is not a good solution.
They should get a wrap on the knuckles if they used a const_cast in that way: the behaviour on trying to change an object via a const_cast on an object that was originally declared as const is undefined. Even if you manage to conjure up a solution to prevent that, a hostile programmer could still take the address of your object, offset that address (using unsigned char* pointer arithmetic) and modify a data member through that pointer!
So if I were you I wouldn't fight the language. Return a const reference if I were you, as per your original suggestion.
Code static analysis tools / compiler warnings / code reviews / human resource departments will help you keep other collaborative programmers on the straight and narrow.
Is there any accepted way in C++ to differentiate between const references to immutable objects vs. mutable ones?
e.g.
class DataBuffer {
// ...
};
class Params {
// ...
};
class C {
public:
// Given references must be valid during instance lifetime.
C(const Params& immutableParameters, const DataBuffer& mutableDataBuffer) :
m_immutableParameters{immutableParameters},
m_mutableDataBuffer{mutableDataBuffer}
{
}
void processBuffer();
private:
const Params& m_immutableParameters;
const DataBuffer& m_mutableDataBuffer;
};
Here the semantic difference is given just in the names.
The problem is that const& instance variables only let you know the object won't be modified by the instance. There is no distinction in the interface whether or not they may be modified elsewhere, which I think is a useful feature to be able to describe in the interface.
Expressing this through the type-system would help make interfaces clearer, allow the compiler to catch errors (e.g. accidentally modifying parameters handed to a C instance, outside of the instance, in the example above), and possibly help with compiler optimizations.
Assuming that the answer is that the distinction isn't possible in C++, maybe there is something close which can be achieved with some templates magic?
Immutability is not part of the C++ type system. As such, you cannot differentiate between immutable objects and mutable ones. And even if you could, std::as_const will always ruin your attempt to do so.
If you are writing an interface that requires immutability of objects, the easiest way to handle this is to invoke the Fundamental Theorem of Software Engineering: "We can solve any problem by introducing an extra level of indirection." So make immutability part of the type system. For example (FYI: uses some small C++17 library stuff):
template<typename T>
class immutable
{
public:
template<typename ...Args>
immutable(std::in_place_t, Args &&...args) t(std::forward<Args>(args)...) {}
immutable() = default;
~immutable() = default;
immutable(const immutable &) = default;
//Not moveable.
immutable(immutable &&) = delete;
//Not assignable.
immutable operator=(const immutable &) = delete;
immutable operator=(immutable &&) = delete;
const T* operator->() const {return &t;}
const T& operator*() const {return t;}
private:
const T t;
};
With this type, the internal T will be immutable regardless of how the user declares their immutable<T>. Your C class should now take an immutable<Params> by const&. And since immutable<T> cannot be constructed from a copy or move of an existing T, the user is forced to use immutable<Params> whenever they want to pass that as a parameter.
Of course, your biggest danger is that they'll pass a temporary. But that was a problem you already needed to solve.
I don't know the reason, but here's how you can do it:
struct C {
template<typename T, typename T2>
C(T&&, const T2&&) = delete;
C(const Params&, const DataBuffer&) { /*...*/ }
};
By declaring a constructor that takes any argument by non-const reference, it will always be a better match than the constructor taking const&, as a cv-qualifier doesn't have to be added.
The const& constructor is a better match when passing a const parameters, as the cv-qualifier doesn't have to be removed.
DataBuffer db;
const Params cp;
C c{ cp, db }; // ok, second constructor call is chosen
Params p;
C c2{ p, db }; // error, constructor is deleted
Due note that, as #IgorTandetnik said, you can break your requirement easily:
Params pa;
const Params& ref_pa = pa;
C c3{ ref_pa, db }; // ok, but shouldn't compile.
As previous answers, C++ doesn't have the concept of "immutable". #Rakete1111 gave you the answer I would have used. However, Visual Studio will put global const variable in .rdata segment, where other variables will go to .data. The .rdata segment will generate a fault when trying to write.
If you need a run time test whether an object is read only, use a signal handler, like this:
#include <csignal>
const int l_ci = 42;
int l_i = 43;
class AV {};
void segv_handler(int signal) {
throw AV{};
}
template <typename T>
bool is_mutable(const T& t)
{
T* pt = const_cast<int*>(&t);
try {
*pt = T();
}
catch (AV av) {
return false;
}
return true;
}
void test_const()
{
auto prev_handler = std::signal(SIGSEGV, segv_handler);
is_mutable(l_i);
is_mutable(l_ci);
}
What you need is not a const reference, but a const object. Value semantics solve your problem. Nobody can modify a const object. While a reference is only const where it is marked const, because the referenced object may not be const. Take that for example :
int a;
int const& b = a;
// b = 4; <-- not compiling, the reference is const
Above, a is int, and b is a reference to const int. While a is not const, the language permit the reference to const to be bound on a non const object. So it's a reference to const object that is bound to a mutable object. The type system won't allow you to modify the mutable object through the reference, because it may have been bound to a const object. In our case it isn't, but the tribe don't change. However, even declaration of a reference to const won't change the original declaration. The int a is still a mutable object. a may still change value:
a = 7;
This is valid, whatever references or other kind of variables have been declared. A variable declared as int (no const) can change, and nothing can prevent it from changing. Heck, even another program like cheat engine can change the value of a mutable variable. Even if you had rules in the language to guarantee that it won't be modified, there is nothing they will prevent the mutable variable from changing values. In any language. In machine language, a mutable value is permitted to change. However, maybe some API of the operating system can help you change the mutability of memory regions.
What can you do to solve this problem now?
If you want to be 100% sure an object won't be modified, you must have immutable data. You usually declare immutable objects with the const keyword :
const int a = 8;
int const& b = a;
// a cannot change, and b is guaranteed to be equal to 8 at this point.
If you don't want a to be immutable and still guarantee b to not change, use values instead of references :
int a = 8;
const int b = a;
a = 9;
// The value of b is still 8, and is guaranteed to not change.
Here, value sematic can help you have what you want.
Then const reference are there for what? There are there to express what you are going to do with the reference, and help enforce what can change where.
As the question has been further clarified, no there is no way to determine if the reference has been bound to a mutable or immutable object in the first place. There is, however, some tricks you can have to differentiate the mutability.
You see, if you want more information about the mutability to be passed along with the instance, you can store that information in the type.
template<typename T, bool mut>
struct maybe_immutable : T {
using T::T;
static constexpr auto mutable = mut;
};
// v--- you must sync them --v
const maybe_immutable<int, false> obj;
This is the most simple way to implement it, but a naive one too. The contained data will be conditionally immutable, but it forces you to sync template parameter and constness. However, the solution allows you to do this :
template<typename T>
void do_something(const T& object) {
if(object.mutable) {
// initially mutable
} else {
// initially const
}
}
I hope I understand you question correct it is not as explicit as so to say "D language" but with const r-value references you can make immutable parameters.
What I understand from immutable is forexample
void foo ( const int&& immutableVar );
foo(4);-> is ok
int a = 5;
foo(a);->is not ok
I'd appreciate if anybody could give me a hint on how to test for trivial copy-ability of a functor (lambdas are meant to be used). As explained in this question it is implementation defined whether lambda is trivially copyable. For example for the code shown at the end of this question gcc (5.4) and msvc (2015) both fire assertions that these are not trivially copy assignable.
I expected these types of lambdas to be represented by struct keeping this pointer and having copy of every captured value (if any). So they all appear to be trivially copyable - at least for the case when the captured values are trivially copyable.
My real use case driving this question is that I'm using a callback (minimalistic version of std::function that does not allocate and is meant for very simple functors) that keeps a fixed buffer in which it copy constructs (in place) passed functor. Then I expected to be able to copy/assign these callbacks but in order for this to work (out of the box) simple mem-copying of these fixed buffers should be equivalent to coping/assigning of the functors kept in them.
So I have two questions:
Imagine that in test_functor() below I do new placement e.g. like
new (&buffer) F(functor)
Is it safe to just memcopy this buffer for the kind of lambdas shown below? I expect this should be so since for all cases there is only this pointer captured or values captured are trivially copyable, but it would be nice if somebody could confirm this.
How can I test whether simple copying of memory where functor is kept is equivalent to copying of the functor? If the answer for the first question is positive then std::is_trivially_copy_assignable not the right answer.
#include <type_traits>
template <typename F>
void test_functor(const F& functor)
{
static_assert(std::is_trivially_destructible<F>::value,
"Functor not trivially destructible");
static_assert(std::is_trivially_copy_constructible<F>::value,
"Functor not trivially copy constructible");
static_assert(std::is_trivially_copy_assignable<F>::value,
"Functor not trivially copy assignable");
}
struct A
{
void test() { test_functor([this]() { }); }
};
struct B
{
void test() { test_functor([this](int v) { value = v; }); }
int value;
};
struct C
{
void test(int v) { test_functor([=]() { value = v; }); }
int value;
};
int main()
{
A a;
B b;
C c;
a.test();
b.test();
c.test(1);
return 0;
}
No it is not safe. If the compiler says something cannot be trivially copied, it cannot be.
It might work. But it working doesn't mean it is safe.
Even if it works today, but tomorrow it stops working after a the compiler updates.
The fix is pretty simple. Write a SBO type (small buffer optimization) that doesn't require trivially copiable.
template<std::size_t S, std::size_t A>
struct SBO {
void(*destroy)(SBO*) = nullptr;
// void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr;
void(*move_ctor)(SBO* src, SBO* dest) = nullptr;
std::aligned_storage_t< S, A > buffer;
void clear() {
auto d = destroy;
destroy = nullptr;
// copy_ctor = nullptr;
move_ctor = nullptr;
if (d) d(this);
}
template<class T, class...Args>
T* emplace( Args&&... args ) {
static_assert( sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment" );
T* r = new( (void*)&buffer ) T(std::forward<Args>(args)...);
destroy = [](SBO* buffer) {
((T*)&buffer->buffer)->~T();
};
// do you need a copy ctor? If not, don't include this:
//copy_ctor = [](SBO const* src, SBO* dest) {
// auto s = (T const*)&src.buffer;
// dest->clear();
// dest->emplace<T>( *s );
//};
move_ctor = [](SBO* src, SBO* dest) {
auto* s = (T*)&src->buffer;
dest->clear();
dest->emplace<T>( std::move(*s) );
src->clear();
};
return r;
}
SBO() = default;
SBO(SBO&& o) {
if (o.move_ctor) {
o.move_ctor(&o, this);
}
}
SBO& operator=(SBO&& o) {
if (this == &o) return *this; // self assign clear, which seems surprising
if (o.move_ctor) {
o.move_ctor(&o, this);
}
return *this;
}
// do you need a copy ctor? If so, implement `SBO const&` ctor/assign
};
live example.
Now here is the punchline. std::function almost certainly already does this for you.
Put a small type with a no-throw move and construct in a std::function and ask if the creation could throw. I'm guessing your implementation will use a SBO to store the type in there.
MSVC 2015 I think has enough room for a lambda storing two std::strings.
The overhead to do things right is modest (two pointers, and a little indirection). You can drop the storage cost down to one pointer per instance at the cost of more indirection (stick the table into a "manual vtable" stored as a static local in a factory function: I can provide a link to examples if that doesn't light a bulb), but with 2 erased methods might as well store them locally (at 3+ consider the static table) unless space is at a premium.
You are already "erasing" invocation, which basically requires storing a function pointer, adding move (and maybe copy) and destroy isn't that much more overhead.
I have a "sum" class which holds two references to existing ints (say). I want to create a "copy" method which deep copies the ints. I thought I would never have to manually delete objects in my code, thanks to smart pointers, but I had to in this solution. Moreover, it is too complicated for a so trivial task (which I need to repeat for several classes). Is there a more straightforward solution?
Note: I don't want to add a bool member (flag) to each objects to determine if the ints must be deleted (in my case, it's not a better overhead than the std::set check overhead in the destructor)
#include <set>
struct sum {
const int &a, &b;
static std::set<const int*> allocated_ints;
sum(const int& a, const int&b): a(a), b(b) {}
sum copy() const {
sum res(*new const int(a), *new const int(b));
allocated_ints.insert(&res.a);
allocated_ints.insert(&res.b);
return res;
}
~sum() {
if (allocated_ints.count(&this->a)) {
delete &this->a;
delete &this->b;
allocated_ints.erase(&this->a);
allocated_ints.erase(&this->b);
}
}
};
std::set<const int*> sum::allocated_ints;
What's the point of a "deep" copy of constants? The constants are going to have the same value no matter what! So just copy (i.e. alias) the const-references:
struct Foo
{
const int & n;
Foo(const int & m) : n(m) { }
Foo(const Foo & rhs) : n(rhs.n) { }
Foo copy() const { Foo f(*this); /* ... */ return f; }
// ...
};
If you're worried about dangling references when returning a copy from a function with a reference to a local variable, then don't make the class have const references, but copies. That way you naturally give your class the copy semantics that you seem to be after anyway.
If you were thinking that you could make a hybrid which is either non-owning or becomes owning depending on how you use it, then I'd say that's bad design that you should avoid. Decide whether your class has ownership over the data or not and then roll with it.
I think you're mixing-up two incompatible concepts.
If you initialize by reference you should refer to existing object whose lifetime is already defined and should be longer than your objects.
If you want to create a copy of your object, since it refers to something, your copy will also refer to that something.
If you want to own yourself dynamic supplied objects, you should use pointers for that, and acquire them as pointers (and delete them on destruction). A copy can then deep-create copies of the pointed objects (or can share them using reference counting or shared_ptr).
You are -in fact- making up a mixing of the two things, resulting in possible problems: think to:
int main()
{
const int x=5; //whatever it is
Foo foo(x);
// ...
} //danger here! ~Foo() will delete x
The references are not deep copied, because they point to an object. Therefore, your code fixed should look like this :
struct sum {
const int &a, &b;
sum(const int& a, const int&b): a(a), b(b) {}
sum copy() const {
sum res(a,b);
return res;
}
~sum() {
}
};
I have a class that should hold a reference to some data, without owning that data (i.e. the actual data is guaranteed not to go out of scope). In particular, the class cannot make a copy – the data is easily several gigabytes in size.
Now, the usual implementation (I assume) is to have a reference to the data:
struct holder_ref {
type const& value;
holder_ref(type const& value) : value(value) { }
};
(Please note that the constness has absolutely no bearing on the problem).
Now, I absolutely need this class to be assignable (i.e. have a working operator =). I thought this was a fairly common problem but I can’t remember how (if ever) I’ve solved it before.
The problem is that a reference cannot be assigned and there’s simply no way around this. The only solution I’ve come up with uses placement new in place of the assignment operator:
// x = other_x; gets replaced with:
x.~T();
new (&x) T(other_x);
Now, this works and is standard compliant. But it sure is ugly. No – inacceptable.
So I’m searching for alternatives. One idea is to use pointers, but I’m unsure whether my constructor is actually guaranteed to work (and passing a pointer is impossible due to the interface I have to adhere to):
struct holder_ptr {
type const* value;
// Is this legal?
holder_ptr(type const& value = 0) : value(&value) { }
};
But I’d rather use a reference, if at all possible. Only – how to implement the assignment operator?
struct holder_ref {
type const& value;
holder_ref(type const& value = 0) : value(value) { }
holder_ref& operator =(holder_ref const& other) {
// Now what?!
return *this;
}
};
As a test case, consider the following code:
int main() {
int const TEST1 = 23;
int const TEST2 = 13;
int const TEST3 = 42;
std::vector<holder_ptr> hptr(1);
std::vector<holder_ref> href(2);
// Variant 1. Pointer.
hptr[0] = holder_ptr(TEST1);
// Variant 2. Placement new.
href[0].~holder_ref();
new (&href[0]) holder_ref(TEST2);
// Variant 3. ???
href[1] = holder_ref(TEST3);
assert(*hptr[0].value == TEST1); // Works (?)
assert(href[0].value == TEST2); // Works
assert(href[1].value == TEST3); // BOOM!
}
(Also, just to make this clear – the type we’re talking about is non-POD and I need a standard compliant solution.)
I don't see anything wrong with using a holder_ptr. It can be implemented something like so:
struct bad_holder : std::exception { };
struct holder_ptr {
holder_ptr() : value(0) { }
holder_ptr(type const& value) : value(&value) { }
type const& get() {
if (value == 0) throw bad_holder();
return *value;
}
private:
type const* value;
};
So long as you always assign to the pointer from a reference, you know that you have a valid object (that, or you ended up with a "null reference" previously, in which case you have other, bigger problems since you'll already have invoked undefined behavior).
With this solution, the interface is implemented entirely in terms of references, but under the hood a pointer is used so that the type is assignable. The use of references in the interface ensures there are none of the concerns that come with using pointers (namely, you never have to worry whether the pointer is null).
Edit: I've updated the example to allow for the holder to be default constructible.
I'd use the pointer holder. But if you are dead set against that, how about hiding your placement new operator=:
holder_ref& operator =(holder_ref const& other) {
new (this) holder_ref(other);
return *this;
}
Is a TR1 weak_ptr standard compliant enough?