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;
};
Related
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*.
I have the following class:
class Data;
class A
{
public:
A(Data& _data) : data(_data) {}
Data& getData() {return data;}
const Data& getData() const {return data;}
private:
Data& data;
};
Now imagine I need to keep not one, but multiple instances of Data. I keep them in a vector of reference wrappers, but I would also like to keep the const correctness: pass the data as unmodifiable in const context.
class A
{
public:
void addData(Data& _data) {data.push_back(std::ref(_data));}
const std::vector<std::reference_wrapper<Data>>& getData() {return data;}
//doesn't compile
//const std::vector<std::reference_wrapper<const Data>>& getData() const {return data;}
private:
std::vector<std::reference_wrapper<Data>> data;
}
How to implement this without having physical copying of the data? I.e. I don't want to return a copy of the vector by value and I don't want to keep two separate vectors in class A. Both are performance-impacting solutions for what is basically just a semantic problem.
Here's a const propagating reference_wrapper, based on cppreference's possible implementation
#include <utility>
#include <functional>
#include <type_traits>
namespace detail {
template <class T> T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>> && !std::is_same_v<reference_wrapper<const T>, std::remove_cvref_t<U>>>()
)>
reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
reference_wrapper(reference_wrapper&) noexcept = default;
reference_wrapper(reference_wrapper&&) noexcept = default;
// assignment
reference_wrapper& operator=(reference_wrapper& x) noexcept = default;
reference_wrapper& operator=(reference_wrapper&& x) noexcept = default;
// access
operator T& () noexcept { return *_ptr; }
T& get() noexcept { return *_ptr; }
operator const T& () const noexcept { return *_ptr; }
const T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
std::invoke_result_t<T&, ArgTypes...>
operator() ( ArgTypes&&... args ) {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
template< class... ArgTypes >
std::invoke_result_t<const T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
T* _ptr;
};
template <class T>
class reference_wrapper<const T> {
public:
// types
typedef const T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<const T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>> && !std::is_same_v<reference_wrapper<T>, std::remove_cvref_t<U>>>()
)>
reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<const T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<const T>(std::forward<U>(u)))) {}
reference_wrapper(const reference_wrapper<T>& o) noexcept
: _ptr(std::addressof(o.get())) {}
reference_wrapper(const reference_wrapper&) noexcept = default;
reference_wrapper(reference_wrapper&&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
reference_wrapper& operator=(reference_wrapper&& x) noexcept = default;
// access
operator const T& () const noexcept { return *_ptr; }
const T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
std::invoke_result_t<const T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
const T* _ptr;
};
// deduction guides
template<class T>
reference_wrapper(T&) -> reference_wrapper<T>;
You can then add const qualified access via span.
class A
{
public:
void addData(Data& _data) {data.emplace_back(_data);}
std::span<reference_wrapper<Data>> getData() { return { data.data(), data.size() }; }
std::span<const reference_wrapper<Data>> getData() const { return { data.data(), data.size() }; }
private:
std::vector<reference_wrapper<Data>> data;
}
Note that you can't copy or move the const reference_wrapper<Data>s from the second getData, and there is only access to const Data &.
Consider the visitor pattern :
struct ConstVisitor {
virtual ~ConstVisitor() = default;
virtual bool visit(const Data & data) = 0;//returns true if search should keep going on
};
void A::accept(ConstVisitor & visitor) const;
This way it does not matter to the outside world what kind of container Data is stored in (here a std::vector). The visitor pattern is very similar to an Enumerator in C#.
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
For example:
struct B{};
struct A {
const B& findB() const { /* some non trivial code */ }
// B& findB() { /* the same non trivial code */ }
B& findB() {
const A& a = *this;
const B& b = a.findB();
return const_cast<B&>(b);
}
};
The thing is I want to avoid repeating the same logic inside the constant findB and non-constant findB member function.
Yes, you can cast the object to const, call the const version, then cast the result to non-const:
return const_cast<B&>(static_cast<const A*>(this)->findB());
Casting away const is safe only when the object in question was not originally declared const. Since you are in a non-const member function, you can know this to be the case, but it depends on the implementation. Consider:
class A {
public:
A(int value) : value(value) {}
// Safe: const int -> const int&
const int& get() const {
return value;
}
// Clearly unsafe: const int -> int&
int& get() {
return const_cast<int&>(static_cast<const A*>(this)->get());
}
private:
const int value;
};
Generally speaking, my member functions are short, so the repetition is tolerable. You can sometimes factor the implementation into a private template member function and call that from both versions.
I think, that using cast here is ok, but if you definitely want to avoid it, you can use some template magic:
struct B
{
B(const B&)
{
std::cout << "oops I copied";
}
B(){}
};
struct A {
public:
A(){}
A(const A&){ std::cout << "a is copied:(\n";}
const B& findB() const { return getter(*this); }
B& findB() { return getter(*this); }
private:
template <typename T, typename V>
struct same_const
{
typedef V& type;
};
template <typename T, typename V>
struct same_const<const T, V>
{
typedef const V& type;
};
template <typename T>
static typename same_const<T,B>::type getter(T& t) { return t.b;}
B b;
};
int main()
{
A a;
const A a_const;
const B& b1 = a.findB();
B& b2 = a.findB();
const B& b3 = a_const.findB();
//B& b4 = a_const.findB();
}
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?