Suppose I have a class Option:
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
if (val_ == nullptr) {
throw std::out_of_range("get on empty Option");
}
return *val_;
}
const T & getOrElse(const T &x) const
{
return val_ == nullptr ? x : *val_;
}
private:
std::shared_ptr<T> val_;
};
The argument passed to Option::getOrElse is the default value to return when this Option is empty:
Option<int> x; // empty
int y = 123;
x.getOrElse(y); // == 123
However, I think the following code is not safe:
Option<int> x;
x.getOrElse(123); // reference to temporary variable!
A safer way would be to return by value from Option::getOrElse, but that would be wasteful when the Option is non-empty. Can I work around this somehow?
UPDATE: I'm thinking about perhaps overloading on the argument type (lvalue/rvalue) of getOrElse, but haven't figured out exactly how to do so.
UPDATE 2: Maybe this?
T getOrElse(T &&x) const { ... }
const T & getOrElse(const T &x) const { ... }
But I think this might be ambiguous because both lvalue and rvalue arguments fit the second version.
However, I think the following code is not safe:
Option<int> x;
x.getOrElse(123); // reference to temporary variable!
You are correct. This is why std::optional::value_or() returns a T and not a T& or T const&. As per the rationale in N3672:
It has been argued that the function should return by constant reference rather than value, which would avoid copy overhead in certain situations:
void observe(const X& x);
optional<X> ox { /* ... */ };
observe( ox.value_or(X{args}) ); // unnecessary copy
However, the benefit of the function value_or is only visible when the optional object is provided as a temporary (without the name); otherwise, a ternary operator is equally useful:
optional<X> ox { /* ... */ };
observe(ox ? *ok : X{args}); // no copy
Also, returning by reference would be likely to render a dangling reference, in case the optional object is disengaged, because the second argument is typically a temporary:
optional<X> ox {nullopt};
auto&& x = ox.value_or(X{args});
cout << x; // x is dangling!
I suggest you follow the same guidelines. If you really need to avoid the copy, use a ternary. This is safe and copyless:
Optional<int> ox = ...;
const int& val = ox ? *ox : 123;
If you really don't, or the Optional is an rvalue anyway, getOrElse() is more concise.
Since users of your class can expect the reference returned from Option::get() to be valid only as along as the the particular instance of the Option object's lifetime, you could reasonably make the same expectation for what is returned from Option::getOrElse().
In that case it might be an acceptable overhead for the object to maintain a collection of things that it needs to keep alive for the client:
#include <list>
#include <memory>
#include <iostream>
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
if (val_ == nullptr) {
throw std::out_of_range("get on empty Option");
}
return *val_;
}
const T & getOrElse(const T &x) const
{
if (val_ == nullptr) {
std::cout << "storing const T &\n";
elses_.push_front(x);
return elses_.front();
}
return *val_;
}
const T & getOrElse(T &&x) const
{
if (val_ == nullptr) {
std::cout << "storing T && by move\n";
elses_.push_front(std::move(x));
return elses_.front();
}
return *val_;
}
private:
std::shared_ptr<T> val_;
mutable std::list<T> elses_;
};
int main()
{
Option<int> x; // empty
int y = 123;
auto rx = x.getOrElse(y); // == 123
auto & rxx = x.getOrElse(42);
std::cout << "rx = " << rx << "\n";
std::cout << "rxx = " << rxx << "\n";
}
The references returned by Option::getOrElse() will be valid for as long as the reference returned from Option::get() would be. Of course, this also means that Option::getOrElse() can throw an exception.
As a small improvement, if the T type can be used as keys for an associative container you could use one of those instead of a std::list and easily avoid storing duplicates.
I'd rather return by reference and let the caller decide, whether he wants to store a reference to or a copy of the returned value.
Can I suggest to re-design this class?
It has a default ctor which can leave the val_ to be nullptr, but it has a get() at the same time which may throw exception because of dereference (*). It also designed to save T in shared_prt but return it as reference.
Let the client to know it's null:
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
return *val_;
}
bool IsNull() const
{
return val_ == nullptr;
}
private:
std::shared_ptr<T> val_;
};
The client code changed from:
Option option;
const T & ref = option.getOrElse(123);
to be:
Option option;
const T & ref = option.IsNull() ? 123 : option.get();
Why I delete the: if (val_ == nullptr) {
Let's make make_shared<> clear:
return a valid pointer, or
throw bad_alloc exception; it does not return null
So IsNull() is also useless, it should be like:
template<typename T>
class Option {
public:
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
return *val_;
}
private:
std::shared_ptr<T> val_;
};
Why to use shared_ptr? option objects can be move or copied several times? or else I prefer to design it like:
template<typename T>
class Option {
public:
Option(T val) noexcept : val_(std::move(val))
{}
const T & get() const
{
return val_;
}
private:
T val_;
};
Related
I'm confused about the lifetime of parameters passed to C++ coroutines.
Answering to a previous question, smart people stated that
The lifetime of a parameter is [...] part of the caller's scope
Now, to follow up, what happens when passing default arguments like
generator my_coroutine(string&& s = string()) {...}
So, if my_coroutine was a normal function, s would be valid throughout its scope. However, this seems to no longer hold if my_coroutine is a coroutine.
In particular the results of the following coroutine-test surprised me:
#include <iostream>
#include <coroutine>
struct Test {
int i = 3;
Test() { std::cout << "test constructed\n";}
Test(const Test&) = delete;
Test(Test&&) = delete;
~Test() { std::cout << "test destructed\n"; }
friend std::ostream& operator<<(std::ostream& os, const Test& t) { return os << t.i; }
};
template<class T>
generator<int> coro_test(T&& t = T()) {
int i = 0;
while(i++ < 3) co_yield i;
if(i == t.i) co_yield 100;
}
int main () {
auto gen = coro_test<Test>();
while(gen.is_valid()) {
std::cout << *gen << "\n";
++gen;
}
return 0;
}
results:
test constructed
test destructed
1
2
3
PS: for completeness, here's my generator:
template<class T>
struct generator {
struct promise_type;
using coro_handle = std::coroutine_handle<promise_type>;
struct promise_type {
T current_value;
auto get_return_object() { return generator{coro_handle::from_promise(*this)}; }
auto initial_suspend() const noexcept { return std::suspend_never{}; }
auto final_suspend() const noexcept { return std::suspend_always{}; }
void unhandled_exception() const { std::terminate(); }
template<class Q>
auto yield_value(Q&& value) {
current_value = std::forward<Q>(value);
return std::suspend_always{};
}
};
private:
coro_handle coro;
generator(coro_handle h): coro(h) {}
public:
bool is_valid() const { return !coro.done(); }
generator& operator++() { if(is_valid()) coro.resume(); return *this; }
T& operator*() { return coro.promise().current_value; }
const T& operator*() const { return coro.promise().current_value; }
generator(const generator&) = delete;
generator& operator=(const generator&) = delete;
~generator() { if(coro) coro.destroy(); }
};
As pointed out in said "previous question", the first thing that happens in a coroutine is that parameters are "copied" into storage owned by the coroutine. However, the "copy" is ultimately initialized based on the type declared in the signature. That is, if a parameter is a reference, then the "copy" of that parameter is also a reference.
So a coroutine function that takes reference parameters is much like any kind of asynchronous function that takes reference parameters: the caller must ensure that the referenced object continues to exist throughout the time that the object will be used. A default parameter which initializes a reference is a circumstance that the caller cannot control the lifetime of (other than providing an explicit parameter).
You created an API that is inherently broken. Don't do that. Indeed, it's best to avoid passing references to async functions of any kind, but if you do, never give them default parameters.
So I'm making a bitmask class that stores a reference to an std::byte as a member and the index of the individual bit to allow accessing the value of that bit and also assigning to that bit. I also want it to be possible for the value of the std::byte passed to optionally be a const, and if it is a const, I want the class itself to be considered a const or at least make sure operations that may change the underlying value of the std::byte (such as assignment) do not work. However I don't see a way to implement it without copypasting code which I consider to be too complicated. Is there an easier way to get around this? This is my current code for the bitmask class:
class bitmask
{
public:
bitmask(std::byte &chunk, std::uint_fast8_t index) noexcept
: _chunk(chunk), _index(index){};
bitmask(bitmask const &source) = default;
operator bool() const noexcept
{
return static_cast<bool>((_chunk >> (7 - _index)) & std::byte{1});
}
bitmask &operator=(bool val) noexcept
{
_chunk = ((_chunk & ~(std::byte{1} << (7 - _index))) |
(std::byte{val} << (7 - _index)));
return *this;
}
private:
std::byte &_chunk;
std::uint_fast8_t const _index;
};
What I want is to basically make a variant of it where chunk is a const reference and the assignment operator doesn't exist, without copy-pasting existing code to avoid reptition.
PS: I don't mind using any C++ standard, including C++20, as long as it solves the problem elegantly.
One option is to turn bitmask into a template and use SFINAE + type traits to alter the behavior:
// vvv defaults to non-const, change if desired
template<typename Chunk = std::byte>
class bitmask
{
static_assert(std::is_same_v<std::remove_const_t<Chunk>, std::byte>);
public:
bitmask(Chunk &chunk, std::uint_fast8_t index) noexcept
: _chunk(chunk), _index(index){};
bitmask(bitmask const &source) = default;
operator bool() const noexcept
{
return static_cast<bool>((_chunk >> (7 - _index)) & std::byte{1});
}
template<bool Enable = !std::is_const_v<Chunk>, typename = std::enable_if_t<Enable>>
bitmask &operator=(bool val) noexcept
{
_chunk = ((_chunk & ~(std::byte{1} << (7 - _index))) |
(std::byte{val} << (7 - _index)));
return *this;
}
private:
Chunk &_chunk;
std::uint_fast8_t const _index;
};
When using C++17 or newer, template arguments need not be supplied manually as class template argument deduction will infer Chunk based on the argument passed to bitmask's constructor. Earlier versions of C++ can use a make_bitmask factory + type aliases to accomplish similar aesthetics, though unfortunately the const and non-const variants will necessarily have to be spelled out differently.
So, despite there being some really nice answers here, I didn't find any of them particularly elegant, so I decided to delve deeper and solve my own problem. Note that this solution isn't entirely mine, and was originally inspired by #ildjarn 's answer, so props to them as well.
This is how I ended up solving my problem
// Class to mask reference to individual bit
template <bool is_const = false>
class bitmask
{
public:
using ChunkType = std::conditional_t<is_const, std::byte const, std::byte>;
bitmask(ChunkType &chunk, std::uint_fast8_t index) noexcept
: _chunk(chunk), _index(index){};
bitmask(bitmask const &source) = default;
operator bool() const noexcept
{
return static_cast<bool>((_chunk >> (7 - _index)) & std::byte{1});
}
template <typename = std::enable_if_t<!is_const>>
bitmask &operator=(bool val) noexcept
{
_chunk = ((_chunk & ~(std::byte{1} << (7 - _index))) |
(std::byte{val} << (7 - _index)));
return *this;
}
private:
ChunkType &_chunk;
std::uint_fast8_t const _index;
};
bitmask(std::byte &, std::uint_fast8_t)->bitmask<false>;
bitmask(std::byte const &, std::uint_fast8_t)->bitmask<true>;
So basically, the class is a template now which takes a boolean value depending on whether the byte referenced to is a const or not, and I also added template argument deduction hints for the constructor so the constness is automatically deduced. I also made operator= only work if is_const is false.
This is what pointers allow. Either completely constant or completely variable. So, a true-false statement could always be made.A template class that deduces of being constant or not as well.
template<class T>
class overload {
public:
overload(T t): t(t) {
}
~overload() {}
T get() {
if(std::is_const<T>::value)
clog <<"const\t " <<t <<endl;
else if(! std::is_const<T>::value)
clog <<"variable\t " <<t <<endl;
return this->t;
}
T set(T t) {
this->t= t;
}
private:
T t;
};
class test {
public:
test(const int * const _t) : _t(_t) {}
test(int *t) : t(t), _t(NULL) {}
~test() {}
int get() { return *(this->t); }
void set(int *t) { this->t= t; }
const int * const _get() { return (this->_t); }
int __get( ) {
return (_t==NULL)?*t:*_t;
}
//void _set(const int * const _t) { this->_t= _t; }
private:
int *t;
const int *const _t;
};
int main(int argc, char*argv[]) {
int n;
const int m= 99;
n= 100;
overload<int> o(n);
overload<const int> _o(m);
::cout <<o.get() <<endl;
::cout <<_o.get() <<endl;
test t(&n), _t(&m);
::cout <<t.get() <<"\t" <<*_t._get() <<"\t" <<t.__get() <<"\t" <<_t.__get() <<endl;
return 0;
}
How to solve std::map default value for move-only types? It seems like the problem is who owns the object
If the value exists, the map stays owner, and a T const& must be returned
If the value does not exits, the caller will be the owner, and a T (move-constructed from the default value) must be returned.
But the return-type of the function must be the same regardless of where the return value came from. Thus it is impossible to take the default value from a temporary. Am I correct?
You could use std::shared_ptr but that would be cheating.
This can be achieved with a proxy object.
template <typename T>
class PossiblyOwner
{
public:
struct Reference {};
PossiblyOwner(const PossiblyOwner & other)
: m_own(other.m_own),
m_ref(m_own.has_value() ? m_own.value() : other.m_ref)
{}
PossiblyOwner(PossiblyOwner && other)
: m_own(std::move(other.m_own)),
m_ref(m_own.has_value() ? m_own.value() : other.m_ref)
{}
PossiblyOwner(T && val) : m_own(std::move(val)), m_ref(m_own.value()) {}
PossiblyOwner(const T & val) : m_own(val), m_ref(m_own.value()) {}
PossiblyOwner(Reference, const T & val) : m_ref(val) {}
const T& value () const { return m_ref; }
operator const T& () const { return m_ref; }
// convenience operators, possibly also define ->, +, etc.
// but they are not strictly needed
auto operator *() const { return *m_ref; }
private:
std::optional<T> m_own;
const T & m_ref;
};
// Not strictly required
template <typename T>
std::ostream & operator<<(std::ostream & out,
const PossiblyOwner<T> & value)
{
return out << value.value();
}
template <typename Container, typename Key, typename ...DefaultArgs>
auto GetOrDefault(const Container & container, const Key & key,
DefaultArgs ...defaultArgs)
-> PossiblyOwner<decltype(container.find(key)->second)>
{
auto it = container.find(key);
using value_type = decltype(it->second);
using ret_type = PossiblyOwner<value_type>;
if (it == container.end())
return {value_type(std::forward<DefaultArgs>(defaultArgs)...)};
else
return {typename ret_type::Reference{}, it->second};
}
Then the usage can be:
int main()
{
std::map<int, std::unique_ptr<std::string>> mapping;
mapping.emplace(1, std::make_unique<std::string>("one"));
mapping.emplace(2, std::make_unique<std::string>("two"));
mapping.emplace(3, std::make_unique<std::string>("three"));
std::cout << *GetOrDefault(mapping, 0,
std::make_unique<std::string>("zero")) << "\n";
std::cout << *GetOrDefault(mapping, 1,
std::make_unique<std::string>("one1")) << "\n";
std::cout << *GetOrDefault(mapping, 3,
new std::string("three1")) << "\n";
}
Edit
I have noticed that the default copy constructor of PossiblyOwner<T> causes undefined behavior, so I had to define a non-default copy and move constructors.
I am writing some code based on issue 28 smart pointer of more effective c++ as follows. However, it cannot compile:
main.cpp: In instantiation of 'SmartPointer<T>::operator SmartPointer<U>() [with U = MusicProduct; T = Cassette]':
main.cpp:99:17: required from here
main.cpp:48:39: error: invalid initialization of non-const reference of type 'SmartPointer<MusicProduct>&' from an rvalue of type 'SmartPointer<MusicProduct>'
return SmartPointer<U> (ptr_);
^
main.cpp:16:9: note: initializing argument 1 of 'SmartPointer<T>::SmartPointer(SmartPointer<T>&) [with T = MusicProduct]'
SmartPointer(SmartPointer<T>& other)
^
Either of these two changes works:
in the implementation of operator SmartPointer (), create an object and return:
SmartPointer a(ptr_);
return a;
Or, make the parameter of the copy constructor as const
SmartPointer(const SmartPointer& other)
and comment the line
other.ptr_ = nullptr;
Is there any reason why either of the solutions works? Thanks.
#include <iostream>
template <typename T>
class SmartPointer
{
public:
SmartPointer(T* ptr) : ptr_(ptr) {}
~SmartPointer()
{
if (ptr_)
{
delete ptr_;
}
}
SmartPointer(SmartPointer<T>& other)
{
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
SmartPointer<T>& operator = (SmartPointer<T>& other)
{
if (this == &other)
{
return *this;
}
if (ptr_)
{
delete ptr_;
}
ptr_ = other.ptr_;
other.ptr_ = nullptr;
return *this;
}
template <typename U>
operator SmartPointer<U> ()
{
// it works
//SmartPointer<U> a(ptr_);
//return a;
// error
return SmartPointer<U> (ptr_);
}
T& operator * () const
{
return *ptr_;
}
T* operator -> () const
{
return ptr_;
}
private:
T* ptr_ = nullptr;
};
class MusicProduct
{
public:
MusicProduct(const std::string& name) : name_(name) {}
virtual ~MusicProduct() {}
virtual void Play() const = 0;
virtual void ShowName() const
{
std::cout << name_ << std::endl;
}
private:
std::string name_;
};
class Cassette : public MusicProduct
{
public:
Cassette(const std::string& name) : MusicProduct(name) {}
void Play () const
{
std::cout << "play cassette" << std::endl;
}
};
void CallPlay(const SmartPointer<MusicProduct>& sp)
{
sp->Play();
}
int main()
{
SmartPointer<Cassette> a(new Cassette("Zhang"));
a->ShowName();
CallPlay(a);
return 0;
}
That's because your copy ctor has a non-const reference parameter and therefore cannot accept a temporary. Thus
return SmartPointer<X>(y);
won't work. The argument to the return keyword is a temporary, and it needs to be copied, so here the design breaks down.
Since you are using C++11, you can fix this by introducing a move constructor (and move assignment).
You can also make the argument const and designate the ptr_ member as mutable. This will allow you to copy from temporaries and const smart pointers, but for the price of actually mutating them.
I need to create a generic object carrier class. I came up with something simple like
template<typename T>
class ObjectCarrier
{
public:
const T& item() const
{
return item_;
}
void setItem(T& item)
{
item_ = item;
}
private:
T item_;
};
This works well when T has got a default constructor (parameterless). Things gets complicated when T has parameterized constructors. So I rewrote the class like
template<typename T>
class ObjectCarrier
{
public:
const T& item() const
{
return *item_;
}
void setItem(T& item)
{
item_ = new T ( item );
}
private:
T* item_;
};
Changed the item_ variable to T* and created a new instance using the copy constructor of T. Again this worked well until T is a pointer type. I mean ObjectCarrier<Foo*> won't work.
I am wondering how can I design this class so that it works for almost all kind of types. I think I may need to create a traits type specialized for pointers. But unfortunately, I am not able to make that work.
Any help would be great.
The above approaches are way way too complicated. Keep it simple, and just solve the constructor arg problem by using template constructors. Don't use pointers, they will create object lifetime and copying headaches.
Here's an implementation I use a lot. The template constructors will forward arguments for things directly on to the nested object which is convenient. The operator T& values let you pass carrier<T> to functions that take a type T, without expensive copying. You can wrap objects that take up to two arguments with this code.
/* A wrapper of type T */
template <typename T>
struct carrier {
carrier() {}
template <typename A1> carrier(const A1& a1) : value(a1) {}
template <typename A1, typename A2> carrier(const A1& a1, const A2& a2) : value(a1, a2) {}
operator T&() { return value; }
operator const T&() const { return value; }
T value;
};
You can use it like this:
const carrier<point> p1(10,10); // make p1 const to stop people changing it
showPoint(p1); // calls a function that expects a point,
showPoint(p1.value); // access the point directly
You can use template specialization for the T* type and rewrite the methods to suite pointers. You can do something like:
template<typename T>
class ObjectCarrier<T*>
{
public:
const T* item() const
{
return item_;
}
void setItem(T* item)
{
item_ = item;
}
private:
T* item_;
};
There is a design patern that is possibly relevant to this - Memento.
A bit off topic, but bear in mind that as soon as you start newing objects up inside your class, you'll need a way to manage the memory. I'd suggest using an std::auto_ptr at the least. You'll also need to provide a copy constructor and an assignment operator, when using std::auto_ptr.
It might be possible to hold the object by value and still defer its construction with the use of placement new and something like the following:
#include <iostream>
#include <cassert>
template <class T>
class ObjectCarrier
{
public:
ObjectCarrier(): ref(0) {}
ObjectCarrier(const ObjectCarrier& other): ref(0)
{
set_data(other.ref);
}
~ObjectCarrier()
{
clear();
}
const ObjectCarrier& operator = (const ObjectCarrier& other)
{
if (other.empty())
clear();
else
set_data(other.ref);
return *this;
}
void set(const T& value)
{
set_value(value);
}
const T& get() const
{
assert(!empty() && "No object being carried");
return *ref;
}
bool empty() const
{
return ref == 0;
}
void clear()
{
if (!empty()) {
ref->~T();
ref = 0;
}
}
private:
char data[sizeof(T)];
T* ref;
void set_value(const T& value)
{
if (!empty()) {
*ref = value;
}
else {
ref = new (data) T(value);
}
}
void set_data(const T* value)
{
if (value) {
set_value(*value);
}
}
};
int main()
{
ObjectCarrier<int> i;
ObjectCarrier<int> j(i);
i = j;
i.set(10);
std::cout << i.get() << '\n';
j = i;
i.set(20);
std::cout << i.get() << ' ' << j.get() << ' ' << ObjectCarrier<int>(i).get() << '\n';
}
However, I would somewhat question the usefulness of this class. Perhaps the only purpose it could have, would be to act as Boost.Optional.
But if you don't want the class to be able to not hold a value, just give it a parametrized constructor:
template<typename T>
class ObjectCarrier
{
public:
ObjectCarrier(const T& value = T()):
item_(value)
{
}
const T& item() const
{
return item_;
}
void setItem(T& item)
{
item_ = item;
}
private:
T item_;
};
(It's just that this class seems rather useless, unless perhaps as a facade for code that expects variables to have item and setItem methods, rather than, say, an assignment operator.)
boost::optional does something very similar to this (also boost::any, but nevermind).
You can check out how its implemented at: http://cplusplus.co.il/2009/12/04/boost-optional-and-its-internals/ and don't worry - it's pretty straightforward.