I'm trying to help with this question, and I think I found a nice solution. But it's kind of complex due to all the wrapping I need.
I would want to overload operator>> to allow for easy chaining notation. But it's not working as I expected.
I can do:
#include <functional>
#include <memory>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T, typename MemFn>
auto fnc (T* ptr, MemFn memFn)
-> decltype(std::invoke(memFn, std::declval<T>())) {
if (ptr) return std::invoke(memFn, *ptr);
return nullptr;
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
[[maybe_unused]] int* optionalTarget = fnc(myObjectPtr.get(), &MyObject::Get);
}
However, I want to do
#include <functional>
#include <memory>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T, typename MemFn>
auto operator>> (T* ptr, MemFn memFn)
-> decltype(std::invoke(memFn, std::declval<T>())) {
if (ptr) return std::invoke(memFn, *ptr);
return nullptr;
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
[[maybe_unused]] int* optionalTarget = myObjectPtr.get() >> &MyObject::Get;
}
What does work, but what I consider ugly is
#include <functional>
#include <memory>
#include <optional>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T>
auto makeOptRef(T* ptr) -> std::optional< std::reference_wrapper<T>> {
if (ptr) return std::ref(*ptr);
return {};
}
template <typename T, typename MemFn>
auto operator>> (std::optional<std::reference_wrapper <T>> ptr, MemFn memFn)
-> std::optional<std::reference_wrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>> {
if (ptr) return makeOptRef(std::invoke(memFn, *ptr));
return {};
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
std::optional<std::reference_wrapper<MyObject>> myObjOptRef = makeOptRef(myObjectPtr.get());
std::optional<std::reference_wrapper<int>> optionalTarget = myObjOptRef >> &MyObject::Get;
[[maybe_unused]] int output = (optionalTarget) ? optionalTarget->get() : -1;
}
Any pointer type is a built-in type, as are pointers to members. You are trying to overload an operator for two built-in types, that's simply a non-starter. At least one argument must be of a user-defined type, or a reference to it.
You can get it to work by passing a T& instead of a T*.
Related
What's the most convenient way to be able to store either std::shared_ptr or Foo* in the same type?
Foo* a = ...;
std::shared_ptr<Foo> b = ...;
Bar c = a; // Success, Bar type can hold Foo*
Bar d = b; // Success, Bar type can also hold std::shared_ptr<Foo>
std::variant<Foo*, std::shared_ptr< Foo>> is okay, but it's not possible to dereference it directly and that is kind of annoying. Is there a better way?
Just use a std::shared_ptr<Foo>.
While it is rarely useful, you can in fact construct a non-owning non-counting std::shared_ptr:
auto p = std::shared_ptr<Foo>{std::shared_ptr<void>(), raw_pointer};
If you want to cater to weird people disrespecting the abstraction (looking at the reference-counts, to be specific), you could also stash an eternal anchor somewhere and use that:
struct pass_t {
template <class... T>
constexpr int operator()(T&&...) noexcept
{ return 0; }
};
constexpr inline pass_t pass;
inline const std::shared_ptr<void> anchor {nullptr, pass};
auto p = std::shared_ptr<Foo>{anchor, raw_pointer};
Basically the same as Caleth's, but more generic and exposing the variant nature of that new type:
template <class... PointerTypes>
struct PointerHolder : std::variant<PointerTypes...> {
using std::variant<PointerTypes...>::variant;
auto *operator -> () const {
return &operator*();
}
auto &operator * () const {
return std::visit([](auto const &p) -> auto & { return *p; }, *this);
}
};
See it live on Wandbox
Use shared_ptr to hold both the unowned Foo* and the owned Foo*. You choose which is which by using a custom deleter that does nothing:
struct not_owned
{
void
operator()(void*) const
{
}
};
Foo f;
std::shared_ptr<Foo> sp{&f, not_owned{}}; // sp doesn't own the pointer
sp = std::make_shared<Foo>(); // sp does own the pointer
You can just wrap std::variant<Foo*, std::shared_ptr< Foo>> in something that does the right dereferencing
class Bar
{
public:
explicit Bar(Foo* foo) : foo(foo) {}
explicit Bar(std::shared_ptr<Foo> foo) : foo(foo) {}
Foo& operator*() const { return std::visit(deref, foo); }
Foo* operator->() const { return std::visit(arrow, foo); }
private:
struct {
Foo& operator()(Foo* f) { return *f; }
Foo& operator()(std::shared_ptr<Foo> f) { return *f; }
} deref;
struct {
Foo* operator()(Foo* f) { return f; }
Foo* operator()(std::shared_ptr<Foo> f) { return f.get(); }
} arrow;
std::variant<Foo*, std::shared_ptr<Foo>> foo;
};
variant might work with some helpers.
template<class X>
struct is_variant: std::false_type{};
template<class...Ts>
struct is_variant< std::variant<Ts...> >:std::true_type{};
template<class X>
constexpr bool is_variant_v = is_variant<X>{};
template<class F>
struct variant_method {
F f;
template<class Variant> requires (is_variant_v<std::decay_t<Variant>>)
friend auto operator->*( Variant && variant, variant_method const& self ) {
return [&](auto&&...args)->decltype(auto) {
return std::visit( [&](auto&& val)->decltype(auto) {
return self.f( decltype(val)(val), decltype(args)(args)... );
}, std::forward<Variant>(variant) );
};
}
};
template<class F>
variant_method(F&&)->variant_method<std::decay_t<F>>;
now we can do this:
constexpr variant_method dereference{ [](auto&& x)->decltype(auto) { return *x; } };
template<class T>
constexpr variant_method operator_{ [](auto&& x)->T { return static_cast<T>(x); } };
constexpr auto to_bool = operator_<bool>;
and here we go!
struct something {
int x = 0;
};
std::variant<std::shared_ptr<something>, something*> some_ptr;
some_ptr = new something;
if ((some_ptr->*to_bool)())
(some_ptr->*dereference)().x = 7;
some_ptr = std::make_shared<something>();
if ((some_ptr->*to_bool)())
(some_ptr->*dereference)().x = 3;
Live example.
Now we can wrap it up pretty
template<class...Ts>
struct variant_ptr:std::variant<Ts...> {
using base = std::variant<Ts...>;
using base::base;
auto& operator*() const { return (((base const&)*this)->*dereference)(); }
auto& operator*() { return (((base&)*this)->*dereference)(); }
auto* get() const { return std::addressof(**this); }
auto* get() { return std::addressof(**this); }
auto* operator->() const { return get(); }
auto* operator->() { return get(); }
explicit operator bool() const { return (((base const&)*this)->*to_bool)(); }
};
now we do this:
variant_ptr<Foo*, std::shared_ptr<Foo>>
and everything "just works".
Live example.
Of course we can skip all of the variant_method stuff above and just implement std::visit in our variant_ptr.
Oh, and here is a version that gets rid of those base& casts by teaching variant_method::operator->* all about interacting with derived-from-variant types.
template<class...Ts>
constexpr decltype(auto) get_variant_base(std::variant<Ts...> const& x) { return x; }
template<class...Ts>
constexpr decltype(auto) get_variant_base(std::variant<Ts...>& x) { return x; }
template<class...Ts>
constexpr decltype(auto) get_variant_base(std::variant<Ts...>&& x) { return std::move(x); }
constexpr auto get_variant_base_lambda = [](auto&&x)->decltype(get_variant_base(decltype(x)(x))){ return get_variant_base(decltype(x)(x)); };
template<class X>
constexpr bool is_variant_derived_v = std::is_invocable_v< decltype(get_variant_base_lambda), X >;
template<class F>
struct variant_method {
F f;
template<class Variant> requires (is_variant_derived_v<Variant>)
friend auto operator->*( Variant && variant, variant_method const& self ) {
return [&](auto&&...args)->decltype(auto) {
return std::visit( [&](auto&& val)->decltype(auto) {
return self.f( decltype(val)(val), decltype(args)(args)... );
}, get_variant_base(std::forward<Variant>(variant)) );
};
}
};
template<class...Ts>
struct variant_ptr:std::variant<Ts...> {
using base = std::variant<Ts...>;
using base::base;
auto& operator*() const { return ((*this)->*dereference)(); }
auto& operator*() { return ((*this)->*dereference)(); }
auto* get() const { return std::addressof(**this); }
auto* get() { return std::addressof(**this); }
auto* operator->() const { return get(); }
auto* operator->() { return get(); }
explicit operator bool() const { return ((*this)->*to_bool)(); }
};
I have the following class
template<typename T>
class A
{
public:
A(T* d) : ptr(d)
{}
A(const T* d) : ptr(const_cast<T*>(d))
{}
T* Ptr()
{
static_assert(???, "Not allowed when using A(const T* d)");
return ptr;
}
const T* Ptr() const
{
return ptr;
}
private:
T* ptr;
}
How can I achieve that when Ptr() is compiled, I know which constructor was used to create this object? I want to statically assert that when Ptr() is compiled that the consstructor A(T* d) was used:
unsigned char* ptr = new unsigned char[10];
const unsigned char* cptr = new unsigned char[10];
A a(ptr);
A ca(cptr);
a.Ptr(); // Compiles
ca.Ptr(); // Gives compile error
I want to detect compile time if the programmer calls Ptr() when the object of class A was created using a const ptr. Calling Foo when created with const ptr is not allowed
I want to use it like this
void Foo(A<int>& r)
{
....
int* ptr = a.Ptr();
....
}
void Bar(const A<int>& r)
{
...
}
...
A a(ptr);
A ca(cptr);
Bar(a);
Bar(ac);
Foo(a);
Foo(ac);// Gives compile error
The simplest c++17 (as I can tell you are using it anyway to deduce template argument type) approach is to use user defined deduction guides and additional tagging non-type template parameter:
enum Tag { //
NonConstTag, // Additional tag enum
ConstTag //
}; //
template<typename T, Tag TT>
// ^^^^^^
// Additional non-type template parameter to provide
// different type of A in case of calling const parameter
// constructor
class A
{
public:
A(T* d) : ptr(d)
{}
A(const T* d) : ptr(const_cast<T*>(d))
{}
T* Ptr()
{
static_assert(TT == NonConstTag, "Not allowed when using A(const T* d)");
return ptr;
}
const T* Ptr() const
{
return ptr;
}
private:
T* ptr;
};
template<typename T> //
A(T* d) -> A<T, NonConstTag>; //
// Deduction guides
template<typename T> //
A(const T* d) -> A<T, ConstTag>; //
int main() {
unsigned char* ptr = new unsigned char[10];
const unsigned char* cptr = new unsigned char[10];
A a(ptr);
A ca(cptr);
a.Ptr(); // Compiles
//ca.Ptr(); // Gives compile error
}
[live demo]
Edit:
A little improvement to satisfy const correctness:
enum Tag {
NonConstTag,
ConstTag
};
template<typename T, Tag TT>
class A
{
public:
A(T* d) : ptr(d), cptr(d)
{}
A(const T* d) : ptr(nullptr), cptr(d)
{}
T* Ptr()
{
static_assert(TT == NonConstTag, "Not allowed when using A(const T* d)");
return ptr;
}
const T* Ptr() const
{
return cptr;
}
private:
T* ptr;
const T* cptr;
};
template<typename T>
A(T* d) -> A<T, NonConstTag>;
template<typename T>
A(const T* d) -> A<T, ConstTag>;
int main() {
unsigned char* ptr = new unsigned char[10];
const unsigned char* cptr = new unsigned char[10];
A a(ptr);
A ca(cptr);
a.Ptr(); // Compiles
//ca.Ptr(); // Gives compile error
}
[live demo]
If you have an IDE it may allow you to jump to the declaration that corresponds to the call. (YouCompleteMe does this in Vim with :YcmCompleter GoTo; I believe Visual Studio has F12 or Alt-F12, if I remember correctly).
Besides that, if you want to detect it runtime, set a flag:
template <typename T> class A {
public:
A(T *) {}
A(const T *) : used_const_arg_ctor(true) {}
private:
bool used_const_arg_ctor = false;
void Foo() {
if (used_const_arg_ctor) {
}
}
};
To actually static assert it, make it a constexpr type:
#include <boost/asio.hpp>
template <typename T> class A {
public:
constexpr A(T *) {}
constexpr A(const T *) : used_const_arg_ctor(true) {}
constexpr bool Foo() const {
return used_const_arg_ctor;
}
private:
bool used_const_arg_ctor = false;
};
int main() {
int const i = 42;
constexpr A<int> a(&i);
static_assert(a.Foo(), "is constructed from const pointer");
}
This has limited usefulness. I'd suggest, instead, to have T reflect constness:
template <typename T> class A {
public:
A(T*d) : _v(d) {}
constexpr bool IsConst() const { return std::is_const<T>::value; }
private:
T* _v;
};
Due to some legacy reasons I'm stuck with MIPS-GCC 4.5.3. But the code which I'm trying to compile uses C++11 nullptr & nullptr_t heavily which is a missing feature in GCC 4.5.3.
After some googling & getting into the usage I ended up creating a nullptr wrapper like below, but unfortunately it doesn't satisfy some of the use case,
namespace std {
class nullptr_t {
public:
nullptr_t() { }
template <typename T> nullptr_t(const T&) { }
template <class T> nullptr_t(const T*) { }
template <class T> nullptr_t(T*) { }
template <typename T, typename U> nullptr_t(const typename T::U*) { }
template<typename T> operator T*() { return 0;}
template<typename T1, typename T2> operator T1 T2::*() { return 0; }
operator int() const { return 0; }
operator unsigned() const { return 0; }
operator bool() const { return false; }
bool operator == (unsigned i) const { return i == 0; }
bool operator != (unsigned i) const { return i != 0; }
bool operator !() const { return true; }
} nullptr = {};
}
using std::nullptr;
template<typename T> struct DummyContainer {
DummyContainer(T* ptr)
: m_ptr(ptr) { }
DummyContainer(std::nullptr_t)
: m_ptr(0) { }
T& operator = (std::nullptr_t) { return *m_ptr; }
private: T* m_ptr;
};
int main(int argc, char** argv)
{
const char* case1 = nullptr; // working
// I think for below case std::unique_ptr has to be modified to take std::nullptr_t during construction & operator =
std::unique_ptr<char> case2 = nullptr; // not working.
DummyContainer<char> case3 = nullptr; // working
case3 = nullptr; //working
unsigned* case4 = argc > 1 ? nullptr : nullptr; //works
unsigned* case5 = argc > 2 ? (unsigned*)0 : nullptr; //not working. (It is the major issue as of now)
return 0;
}
Here the major case is unsigned* case5 = argc > 2 ? (unsigned*)0 : nullptr;
IDEONE snapshot : http://ideone.com/m1mhtB
(Thanks to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf)
Any tips/suggestions will be appreciated :)
(Note: please avoid answers like upgrade your gcc)
Below solution seems to be working,
Original source: https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/wtf/NullPtr.h
namespace std {
class nullptr_t {
public:
// Required in order to create const nullptr_t objects without an
// explicit initializer in GCC 4.5, a la:
//
// const std::nullptr_t nullptr;
nullptr_t() { }
// Make nullptr convertible to any pointer type.
template<typename T> operator T*() const { return 0; }
// Make nullptr convertible to any member pointer type.
template<typename C, typename T> operator T C::*() { return 0; }
private:
// Do not allow taking the address of nullptr.
void operator&();
};
}
const std::nullptr_t nullptr;
IDEOne: http://ideone.com/Bnp6th
Why am I getting this error:
Error 1 error C2662: 'Allocator::Allocate' : cannot convert 'this' pointer from 'const Allocator' to 'Allocator &' ?
Thats code:
/*Allocator.h*/
/*Not finished yet but working*/
#pragma once
template<class T>
class Allocator
{
public:
//typedef T value_type;
typedef T* pointer;
pointer Allocate(std::size_t count);
pointer Construct(void* address, const pointer obj);
template<class FwdIter>
void Destroy_(FwdIter first,FwdIter last);
void Deallocate_(void* where);
Allocator();
~Allocator();
private:
void Destroy_(const T* obj);
};
/*Allocator_impl.hpp*/
#pragma once
#include "StdAfx.h"
#include "Allocator.h"
template<class T>
Allocator<T>::Allocator()
{
}
template<class T>
Allocator<T>::~Allocator()
{
/*Destroy();
Deallocate();*/
}
template<class T>
typename Allocator<T>::pointer Allocator<T>::Allocate(std::size_t count)
{
return static_cast<pointer>(::operator new(sizeof(value_type) * count));
}
template<class T>
typename Allocator<T>::pointer Allocator<T>::Construct(void* address, const pointer obj)
{
return new (address) T(*obj);
}
//template<class T>
//void Allocator<T>::Destroy()
//{
// //Destroy_(addressBegin_, addressBegin_ + size_);
//}
template<class T>
void Allocator<T>::Destroy_(const T* obj)
{
obj->~T();
}
template<class T>
template<class FwdIter>
void Allocator<T>::Destroy_(FwdIter first,FwdIter last)
{
while (first != last)
{
Destroy_(&*first);
++first;
}
}
template<class T>
void Allocator<T>::Deallocate_(void* address)
{
::operator delete(address);
}
//template<class T>
//void Allocator<T>::Deallocate()
//{
// //Deallocate_(addressBegin_);
//}
/*Toy.h*/
#pragma once
#include "Allocator_impl.hpp"
/*As a base to managed memory*/
template<class T, class A = Allocator<T>>
class ToyBase
{
typedef T* pointer;
private:
A alloc_;
protected:
//--------------------------------------COMMENT HERE
pointer Allocate(const std::size_t)const;<------------When invoking this fnc from
explicit ToyBase();
virtual ~ToyBase();
};
template<class T, class A>
ToyBase<T,A>::ToyBase()
{}
template<class T, class A>
ToyBase<T,A>::~ToyBase()
{}
//--------------------------------------AND COMMENT HERE
template<class T, class A>
typename ToyBase<T,A>::pointer ToyBase<T,A>::Allocate(const std::size_t count)const
{
return alloc_.Allocate(count);<-----------here
}
/*
But when I remove const from fnc decl. it works. I do not understand it as I do not change an object merely invoke fnc on its member.
*/
template<class T>
class ToyRepresentation : private ToyBase<T>
{
public:
typedef T value_type;
typedef T* pointer;
ToyRepresentation(const std::size_t = 0);
void Push(T*);
void Pop();
void GetSize()const;
void GetCapacity()const;
void SetCapacity(const std::size_t);
void Reset();
private:
pointer data_;
std::size_t size_;
std::size_t capacity_;
static unsigned TOTAL_; //total number of created objects
};
template<class T>
unsigned ToyRepresentation<T>::TOTAL_ = 0;
template<class T>
ToyRepresentation<T>::ToyRepresentation(const std::size_t count = 0): ToyBase<T>(), data_(Allocate(count)), size_(0), capacity_(count)
{
}
/*tmain*/
#include "stdafx.h"
#include "Toy.h"
int _tmain(int argc, _TCHAR* argv[])
{
try
{
ToyRepresentation<int> t;
}
catch(const std::exception&)
{
}
return 0;
}
Comments to interesting lines are marked in code. Thanks.
alloc_.Allocate is not a const method. You can "fix" (or hide) this warning by making alloc_ mutable, although this should not be done without understanding of why the compiler is warning you.
Not sure why the method that calls this needs to be const anyhow. Allocation is not something that typically is expected to leave its context unchanged.
ToyBase<T,A>::Allocate is const qualified which means that you cannot invoke any non-const methods on any members of this as they too are const-qualified now.
Try FAQ 18.10.
Some smart pointer templates, such as boost::shared_ptr, may be instantiated with void to hold an arbitrary object:
http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/sp_techniques.html#pvoid
Below is a minimal scoped_ptr implementation. When instantiated with void, the compiler complains about an illegal "reference to void" being formed in the dereference operator. It seems the "substitution failure is not an error" (SFINAE) rule does not cover this situation.
How then is it possible to implement a scoped_ptr? In particular, is there an alternative to writing a template specialization? This would cause large code reduplication with a realistic smart pointer implementation.
#include <cstdlib>
template<typename T>
void destroy(T* ptr)
{
delete ptr;
}
class scoped_ptr_impl_base
{
public:
virtual ~scoped_ptr_impl_base() { }
};
template<typename T, typename F>
class scoped_ptr_impl : public scoped_ptr_impl_base
{
public:
scoped_ptr_impl(T* ptr, F dtor)
: m_ptr(ptr), m_dtor(dtor)
{
}
virtual ~scoped_ptr_impl()
{
m_dtor(m_ptr);
}
private:
T* m_ptr;
F m_dtor;
};
template<typename T>
class scoped_ptr
{
public:
explicit scoped_ptr(T* ptr = 0)
: m_ptr(ptr),
m_impl(new scoped_ptr_impl<T, void (*)(T*)>(&destroy<T>))
{
}
template<typename F>
scoped_ptr(T* ptr, F dtor)
: m_ptr(ptr),
m_impl(new scoped_ptr_impl<T, F>(ptr, dtor))
{
}
~scoped_ptr()
{
delete m_impl;
}
T& operator*()
{
return *m_ptr;
}
T* operator->()
{
return m_ptr;
}
private:
T* m_ptr;
scoped_ptr_impl_base* m_impl;
scoped_ptr(const scoped_ptr&);
scoped_ptr& operator=(const scoped_ptr&);
};
int main()
{
scoped_ptr<void> p(std::malloc(1), std::free);
// scoped_ptr.cpp: In instantiation of `scoped_ptr<void>':
// scoped_ptr.cpp:76: instantiated from here
// scoped_ptr.cpp:56: error: forming reference to void
// (g++ 4.3.3)
return 0;
}
You could use a type trait for the reference type:
template<typename T>
struct type_trait
{
typedef T& reference;
};
template<>
struct type_trait<void>
{
typedef void reference;
};
then in your scoped_ptr_impl :
typename type_trait<T>::reference operator*()
{
return *m_ptr;
}
Not sure if void is the right type in the specialisation though . What type do you want it to return?