I am working on an implementation of a shared pointer. (using C++17, in case it matters)
The only issue is the conversion constructor. I want to be able to static cast a smart_ptr to a smart_ptr of base type.
template<typename U>
inline smart_ptr(const smart_ptr<U>& rhs)
{
...
}
It works, but it will also try to cast a smart_ptr to a smart_ptr of any other type. For example, if I have an overloaded function that can take different kinds of smart_ptr's of unrelated type, I get a compiler error about ambiguous overload. So, I only want the conversion from smart_ptr -> smart_ptr if U is a derived class of T.
This looks like it should work. It compiles, but it does the opposite. It prevents the valid static upcasts from working, but still allows the casting to unrelated types:
template<typename U>
inline local_shared_ptr(typename enable_if<is_base_of<T,U>::value, const local_shared_ptr<U>&>::type rhs)
{
...
}
EDIT:
Got it working, thanks for the help. I choose jarod's solution, since I find template <typename U, enable_if_t<is_base_of<T,U>::value, int> = 0> the most concise. I didn't realize SFINAE could be that concise.
Also, since it was mentioned by Nathan:
Funnily enough, one of the issues I encountered is that I expected the template copy constructor to be called when the right-hand-side is the same type. Apparently, the compiler doesn't consider it an implementation of the copy constructor, and instead the auto-generated copy constructor was being called instead. Same issue for the move constructor and operator=. Not sure if that's a bug with MSVC2019.
U is non deducible with
template<typename U>
local_shared_ptr(enable_if_t<is_base_of<T,U>::value, const local_shared_ptr<U>&> rhs)
{
// ...
}
And as it is a constructor, you even cannot provide template explicitly.
So that constructor is useless.
You can use instead:
Default parameter (the most similar to your attempt IMO):
template <typename U>
local_shared_ptr(const local_shared_ptr<U>& rhs, enable_if_t<is_base_of<T,U>::value, int> = 0)
{
// ...
}
default template parameter (preferred way):
template <typename U, enable_if_t<is_base_of<T,U>::value, int> = 0>
local_shared_ptr(const local_shared_ptr<U>& rhs)
{
// ...
}
And as you use constructor, you cannot use return value.
Put the enable_if in the template parameter list like
template<typename U, std::enable_if_t<std::is_base_of_v<T, U>, bool> = true>
inline smart_ptr(const smart_ptr<U>& rhs)
{
}
And now this will only be called if U is T, or is derived from T. If you don't want to use this if U == T then you can use
template<typename U, std::enable_if_t<std::is_base_of_v<T, U> && !std::is_same_v<T, U>, bool> = true>
inline smart_ptr(const smart_ptr<U>& rhs)
{
}
Related
Assume we have a template class:
class Object<T>
And one instance of a variable:
Object<const IQuestion> qobj1(new Question());
Object<IQuestion> qobj2(new Question());
I would like to make a call to the function areEqual like this:
areEqual(question1, question2).
How to make a call to a function:
bool areEqual(const Object<IQuestion>& rhs, const Object<IQuestion>& rhs) const
Considering that variables are slightly different?
I assume that this somehow can be achieved with static_cast or reinterpret_cast.
The following probably does something similar to what you are looking for:
template<typename T, typename U>
std::enable_if_t<std::is_same<std::decay_t<T>, std::decay_t<U>>::value, bool>
areEqual(const Object<T>& lhs, const Object<U>& rhs) {
// T and U are the same type, put aside cv qualifiers and references
// if you know what's a meaningful way to compare them, do that
// note that here T and U can still have different cv-qualifiers
}
See a minimal, working example on coliru.
The following is an attempt at implementing a shared pointer with a modified semantics of operator==:
template <typename T>
struct deref_shared_ptr: private std::shared_ptr<T> {
using Base = std::shared_ptr<T>;
// ... using statements to include functionality from the base.
bool operator==(const deref_shared_ptr rhs) const {
return (**this == *rhs);
}
};
I am struggling with implementing an equivalent of std::make_shared for this type. This is my attempt:
template< class T, class... Args >
deref_shared_ptr<T> make_deref_shared( Args&&... args ) {
return reinterpret_cast<deref_shared_ptr<T>>(std::make_shared<T>(args...));
}
This does not work: the compiler (g++ 5.4.0) complains about an invalid cast. Why does it not work and what should I do instead of this cast?
You see this compiler error message because the reinterpret_cast cannot make casts through the private inheritance. Please check the following themes on this topic: difference between c++ casts, conversion which may be handled by c-style cast only.
The only way to go through the private inheritance is the c-style cast. So, changing your example as follows makes your example work:
template< class T, class... Args >
deref_shared_ptr<T> make_deref_shared(Args&&... args) {
return (deref_shared_ptr<T>)(std::make_shared<T>(args...));
}
The c-style cast is not safe in the general case since it may work incorrectly in cases of multiple inheritance and some other cases, but AFAIK it's safe in this case.
I suggest your deref_shared_ptr to implement a constructor that receive a std::shared_ptr as parameter, so the conversion would be possible. Right now your compiler has no idea how to make a deref_shared_ptr from a std::shared_ptr. This is exactly what we will teach your compiler to do.
I noticed you add a custom operator== to compare correctly your type with a std::shared_ptr. Here we want to do the same thing but with constructor. We want a constructor that construct correctly with your type with a std::shared_ptr!
The constructor would look like this:
template<typename T>
struct deref_shared_ptr : private std::shared_ptr<T> {
// An alias to the parent may help msvc with templated parent types
using parent = std::shared_ptr<T>;
// Implement a constructor that takes shared_ptr by copy and move
deref_shared_ptr(const parent& ptr) : parent{ptr} {}
deref_shared_ptr(parent&& ptr) : parent{std::move(ptr)} {}
// stuff...
};
Then, the make function becomes trivial to implement:
template<typename T, typename... Args>
deref_shared_ptr<T> make_deref_shared(Args&&... args) {
// Don't forget perfect forwarding here!
return std::make_shared<T>(std::forward<Args>(args)...);
}
EDIT:
Alternatively, if your constructors are not doing any operation, you can make use of inheriting constructors:
template<typename T>
struct deref_shared_ptr : private std::shared_ptr<T> {
using parent = std::shared_ptr<T>;
// Implement constructors
using parent::parent;
// stuff...
};
That would simplify constructor implementations and will make your type compatible by construction with std::shared_ptr.
Disclaimer: I know that using user defined implicit conversions is often discouraged. However, in our project, we need these conversions for various template classes to work well with each other.
I need to define a precedence of user defined conversions, e.g.:
struct X{}
struct Y{}
struct Z{
operator X(){...}
operator Y(){...}
}
void foo(X x){...}
void foo(Y y){...}
// somewhere in some template client code
...{
Z z = ...;
...
foo(z); // WILL NOT COMPILE
}
This will not compile, as the conversion from Z to either X or Y is ambiguous. Is there a way to resolve this ambiguity. I.e, can I somehow tell the compiler: if there is a function overloaded for X and Y, then prefer to cast Z to X instead of failing to compile.
I know that there is no simple way to specify this. But maybe some template magic and a wrapping template struct that I am not aware of might do the trick. I am allowed to change the client code. I cannot use an explicit cast, as the client code is a templated code that is not aware about the type Z and the available overloads of foo.
You can do something like "prioritized call", with a pattern like this:
struct P2 {};
struct P1: P2 {};
template<class A>
void foo(A x, P1, typename std::common_type<X,A>::type* =nullptr)
{ foo(static_cast<X>(x)); }
template<class A>
void foo(A y, P2, typename std::common_type<Y,A>::type* =nullptr)
{ foo(static_cast<Y>(y)); }
template<class A> void foo(A a) { foo(a,P1()); }
being P2 a base for P1 and the call made with P1, if common_type can compile, the first version goes. If it cannot compile, the first version is like not existent (SFINAE) and the second goes. If it cannot compile as well ... if A is just X or just Y the respective original foo is called, otherwise this cannot compile being types incompatible.
Note that you can even generalize the "priority" as
template<size_t N> struct P: P<N+1> {}
template<> struct P<10> {}
declare SFINAE functions taking P<1>, P<2> etc. up to P<10>, and place the root call with P<0>()
Here is a little system that smart casts a variable to to a sequence of types Ts... such that the first element in the list Ts... that the variable implicitly converts to is the one chosen:
namespace details {
template<class...>struct types{using type=types;};
template<class U, class Types, class=void>
struct smart_cast_t:std::false_type {
using type=U;
template<class A>
U operator()(A&& a)const{return std::forward<A>(a);}
};
template<class U, class T0, class...Ts>
struct smart_cast_t<
U, types<T0, Ts...>,
typename std::enable_if<std::is_convertible<U, T0>::value>::type
>:std::true_type
{
using type=T0;
template<class A>
T0 operator()(A&& a)const{return std::forward<A>(a);}
};
template<class U, class T0, class...Ts>
struct smart_cast_t<
U, types<T0, Ts...>,
typename std::enable_if<!std::is_convertible<U, T0>::value>::type
>:smart_cast_t< U, types<Ts...> >
{};
}
template<class... Ts, class U>
auto smart_cast( U&& u )
-> decltype(details::smart_cast_t< U, details::types<Ts...> >{}( std::forward<U>(u) ))
{
return details::smart_cast_t< U, details::types<Ts...> >{}( std::forward<U>(u) );
}
Now, the idea is that we can now modify foo as follows:
void foo_impl(X);
void foo_impl(Y);
template<class A>
void foo(A&& a) {
foo_impl( smart_cast<X, Y>(std::forward<A>(a)) );
}
and foo casts the A to X if possible, and if not to Y.
We could write up an entire system whereby we pass in a description of the overloads of foo in a package like types< types<X,Y> > and an overload set of foo to some magic code, which spits out a dispatcher, but that would be over engineering.
live example
This uses C++11 features. With C++14 we get to drop some verbage in smart_cast.
The design is pretty simple. We create a types bundle to work with bundles of types (just boilerplate).
Then our details::smart_cast_t has a fallback base specialization that just converts our U to U. If we can convert U to the first type, we do so. Otherwise, we recurse on our parent type, possibly terminating in the base specialization.
I hide that in details, so our public function is simple -- it is smart_cast< type1, type2, type3, etc >( expression ). We don't have to pass U the type of the expression, as we deduce it, then pass it into details for the work to be done.
At run time this reduces to a single implicit cast: all the above is what is done at compile time.
About the only downside is that this can result in warnings on some compilers, as we use implicit conversion. Add a static_cast<T0> to the first non-base specialization of smart_cast_t to avoid this.
I inherited smart_cast_t from true_type and false_type needlessly. This means that the value of smart_cast_t<U, types<Ts...>>::value will tell you if U converted to any of Ts..., or was just left alone as a U.
The caller is responsible for rvalue vs lvalue categories. If the cast fails, it will return a U not a U&& if passed an rvalue. The fallback -- to U -- I think will generate better error messages, but if you prefer that smart_cast<int, double>(std::string("hello")) fail to compile instead of returning a std::string, simply remove the operator() from the base specialization of smart_cast_t.
Speaking of which, I also don't work right for smart_cast<int, double>(""). Might need some typename std::decay<U>::types or something in there.
Yes, cast it explicitly:
foo(static_cast<Y>(z));
I try to implement a functor version of static_cast for use in std::bind().
I am aware of Boost ll_static_cast<K>() (see using static_cast with boost::bind), but I am not using Boost right now.
There is a code example in Why do several of the standard operators not have standard functors? but it won't compile on GCC 4.2.1:
template <typename Target>
struct StaticCast
{
template <typename Source>
Target operator()(Source&& source) const
{
return static_cast<Target>(source);
}
}
I managed to get something to compile, but I am not sure it's correct:
template <class Target>
struct StaticCast : public std::unary_function<void, Target> {
template <class Source>
Target operator()(Source& src) const {
return static_cast<Target>(src);
}
};
Can someone tell me if this version is correct, and if this is the case, why I need std::unary_function which is not used in the previous code example?
Usage:
std::vector<BaseObject*> vec; // BaseObject* are known to be of type
// DerivedObject* of course, please don't ask me how or why...
std::for_each(vec.begin(), vec.end(),
std::bind(&DerivedObject::doStuff,
std::bind(StaticCast<DerivedObject*>(), std::placeholders::_1),
"with some string"));
Given the lack of perfect forwarding in C++03, you'll have to make due with overloads:
template<class Target>
struct StaticCast
{
typedef Target result_type;
template<class Source>
Target operator ()(Source& src) const
{
return static_cast<Target>(src);
}
template<class Source>
Target operator ()(Source const& src) const
{
return static_cast<Target>(src);
}
};
Note that I'm explicitly making a typedef for result_type rather than inheriting from std::unary_function<>. The reason is that the first template parameter to std::unary_function<> is supposed to be operator()'s argument type, but because our operator() is a template we can't know this in advance, so it's disingenuous to supply one in the first place (especially void, which would imply that operator() is nullary, when in fact it is unary).
Also, for completeness' sake, here is the correct C++11 version of the functor:
template<class Target>
struct StaticCast
{
template<class Source>
Target operator ()(Source&& source) const
{
return static_cast<Target>(std::forward<Source>(source));
}
}
One reason the first one doesn't work is probably because you are using rvalue references on a compiler that doesn't support C++11.
The reason you need std::unary_function is to enable std::result_of for your class which std::bind uses to deduce the result type, since there is no decltype in C++98.
If you look at std::unary_function you will see that it defines the type result_type from the template arguments you pass, which is in turned used by std::result_of or std::bind directly.
Well, generally your code is bad:
First, you may have troubles with temporary objects and r-values while requesting non-const reference for them
for example
float f = StaticCast<float>()(4);
won't even compile.
Then, you make a copy of object while casting. It may be do not that you want.
Source example free of that disadvantages due to move semantics
I have a subset of a pointer class that look like:
template <typename T>
struct Pointer
{
Pointer();
Pointer(T *const x);
Pointer(const Pointer &x);
template <typename t>
Pointer(const Pointer<t> &x);
operator T *() const;
};
The goal of the last constructor is to allow to pass a Pointer of a subclass, or basically any type that is implicitly convertable to T *. This actual rule is only enforced by the definition of the constructor and the compiler can't actually figure it out by the declaration alone. If I drop it, and try to pass a Pointer<Sub> to a constructor of Pointer<Base>, I get a compile error, despite of the possible path through operator T *().
While it solves the above problem, it creates another one. If I have an overloaded function whose one overload takes a Pointer<UnrelatedClass> and the other takes Pointer<BaseClass>, and I try to invoke it with a Pointer<SubClass>, I get an ambiguity between the two overloads, with the intention, ofcourse, that the latter overload will be called.
Any suggestions? (Hopefully I was clear enough)
The cure for your problem is called SFINAE (substitution failure is not an error)
#include "boost/type_traits/is_convertible.hpp"
#include "boost/utility/enable_if.hpp"
template<typename T>
class Pointer {
...
template<typename U>
Pointer(const Pointer<U> &x,
typename boost::enable_if<
boost::is_convertible<U*,T*>
>::type* =0)
: ...
{
...
}
...
};
If U* is convertible to T* the enable_if will have a typedef member type defaulting to void. Then, everything is fine. If U* is not convertible to T* this typedef member is missing, substitution fails and the constructor template is ignored.
This solves your conversion and ambiguity problems.
In response to the comment: is_convertible looks something like this:
typedef char one; // sizeof == 1 per definition
struct two {char c[2];}; // sizeof != 1
template<typename T, typename U>
class is_convertible {
static T source();
static one sink(U);
static two sink(...);
public:
static const bool value = sizeof(sink(source()))==1;
};
Try to make the constructor in question explicit, e.g.:
template <typename t>
explicit Pointer(const Pointer<t> &x);
And/or remove the operator T *() const; - I think this one will also create an ambiguity.
EDIT
Check the std::auto_ptr interface, and compare with yours. At least they solved the ambiguity.