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
Related
This page states the following:
Given that we have the following factory template function:
template<typename T, typename Arg>
shared_ptr<T> factory(Arg&& arg)
{
return shared_ptr<T>(new T(forward<Arg>(arg)));
}
One can choose among any of the two forward implementations:
forward implementation using remove_reference
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{
return static_cast<S&&>(a);
}
forward implementation without remove_reference
template<class S>
S&& forward(S& a) noexcept
{
return static_cast<S&&>(a);
}
In the tutorial, it's mentioned that:
If you want to dig a little deeper for extra credit, ask yourself this
question: why is the remove_reference in the definition of
std::forward needed? The answer is, it is not really needed at all. If
you use just S& instead of remove_reference::type& in the defintion
of std::forward, you can repeat the case distinction above to convince
yourself that perfect forwarding still works just fine. However, it
works fine only as long as we explicitly specify Arg as the template
argument of std::forward. The purpose of the remove_reference in the
definition of std::forward is to force us to do so.
Why does using remove_reference forces us to specify Arg as the template arg, are there any concrete examples where the difference is illustrated? The code appears to be the same since in both instances of forward, the type is derived from the template argument. I am also new to c++ so sorry if i am missing something obvious here.
Why does using remove_reference forces us to specify Arg as the
template arg
Because having a nested type remove_reference<S>::type as an argument makes a non-deducible context.
This applies for any nested type. For example, if you have
template< class T>
struct Identity
{
using type = T;
};
template< class T> void foo(T&){}
template< class T> void bar(typename Identity<T>::type&){}
then
int x;
foo(x);
bar<int>(x);
will compile, but
int x;
bar(x);
will not.
I have a function that needs to take shared ownership of an argument, but does not modify it.
I have made the argument a shared_ptr<const T> to clearly convey this intent.
template <typename T>
void func(std::shared_ptr<const T> ptr){}
I would like to call this function with a shared_ptr to a non-const T. For example:
auto nonConstInt = std::make_shared<int>();
func(nonConstInt);
However this generates a compile error on VC 2017:
error C2672: 'func': no matching overloaded function found
error C2784: 'void func(std::shared_ptr<const _Ty>)': could not deduce template argument for 'std::shared_ptr<const _Ty>' from 'std::shared_ptr<int>'
note: see declaration of 'func'
Is there a way to make this work without:
Modifying the calls to func. This is part of a larger code refactoring, and I would prefer not to have to use std::const_pointer_cast at every call site.
Defining multiple overloads of func as that seems redundant.
We are currently compiling against the C++14 standard, with plans to move to c++17 soon, if that helps.
template <typename T>
void cfunc(std::shared_ptr<const T> ptr){
// implementation
}
template <typename T>
void func(std::shared_ptr<T> ptr){ return cfunc<T>(std::move(ptr)); }
template <typename T>
void func(std::shared_ptr<const T> ptr){ return cfunc<T>(std::move(ptr)); }
this matches how cbegin works, and the "overloads" are trivial forwarders with nearly zero cost.
Unfortunately, there is no good solution to what you desire. The error occurs because it fails to deduce template argument T. During argument deduction it attempts only a few simple conversations and you cannot influence it in any way.
Think of it: to cast from std::shared_ptr<T> to some std::shared_ptr<const U> it requires to know U, so how should compiler be able to tell that U=T and not some other type? You can always cast to std::shared_ptr<const void>, so why not U=void? So such searches aren't performed at all as in general it is not solvable. Perhaps, hypothetically one could propose a feature where certain user-explicitly-declared casts are attempted for argument deduction but it isn't a part of C++.
Only advise is to write function declaration without const:
template <typename T>
void func(std::shared_ptr<T> ptr){}
You could try to show your intent by making the function into a redirection like:
template <typename T>
void func(std::shared_ptr<T> ptr)
{
func_impl<T>(std::move(ptr));
}
Where func_impl is the implementation function that accepts a std::shared_ptr<const T>. Or even perform const cast directly upon calling func_impl.
Thanks for the replies.
I ended up solving this a slightly different way. I changed the function parameter to just a shared_ptr to any T so that it would allow const types, then I used std::enable_if to restrict the template to types that I care about. (In my case vector<T> and const vector<T>)
The call sites don't need to be modified. The function will compile when called with both shared_ptr<const T> and shared_ptr<T> without needing separate overloads.
Here's a complete example that compiles on VC, GCC, and clang:
#include <iostream>
#include <memory>
#include <vector>
template<typename T>
struct is_vector : public std::false_type{};
template<typename T>
struct is_vector<std::vector<T>> : public std::true_type{};
template<typename T>
struct is_vector<const std::vector<T>> : public std::true_type{};
template <typename ArrayType,
typename std::enable_if_t<is_vector<ArrayType>::value>* = nullptr>
void func( std::shared_ptr<ArrayType> ptr) {
}
int main()
{
std::shared_ptr< const std::vector<int> > constPtr;
std::shared_ptr< std::vector<int> > nonConstPtr;
func(constPtr);
func(nonConstPtr);
}
The only downside is that the non-const instantiation of func will allow non-const methods to be called on the passed-in ptr. In my case a compile error will still be generated since there are some calls to the const version of func and both versions come from the same template.
As the const is only for documentation, make it a comment:
template <class T>
void func(std::shared_ptr</*const*/ T> p) {
}
You could additionally delegate to the version getting a pointer to constant object if the function is hefty enough to make it worthwhile:
template <class T>
void func(std::shared_ptr</*const*/ T> p) {
if (!std::is_const<T>::value) // TODO: constexpr
return func<const T>(std::move(p));
}
No guarantee the compiler will eliminate the move though.
You certainly don't want to be modifying the call sites, but you sure can be modifying the functions themselves - that's what you implied in the question anyway. Something had to be changed somewhere, after all.
Thus:
In C++17 you could use deduction guides and modify call sites, but in a less intrusive manner than with a cast. I'm sure a language lawyer can pitch in about whether adding a deduction guide to the std namespace is allowed. At the very least we can limit the applicability of those deduction guides to the types we care about - it won't work for the others. That's to limit potential for mayhem.
template <typename T>
class allow_const_shared_ptr_cast : public std::integral_constant<bool, false> {};
template <typename T>
static constexpr bool allow_const_shared_ptr_cast_v = allow_const_shared_ptr_cast<T>::value;
template<>
class allow_const_shared_ptr_cast<int> : public std::integral_constant<bool, true> {};
template <typename T>
void func(std::shared_ptr<const T> ptr) {}
namespace std {
template<class Y> shared_ptr(const shared_ptr<Y>&) noexcept
-> shared_ptr<std::enable_if_t<allow_const_shared_ptr_cast_v<Y>, const Y>>;
template<class Y> shared_ptr(shared_ptr<Y>&&) noexcept
-> shared_ptr<std::enable_if_t<allow_const_shared_ptr_cast_v<Y>, const Y>>;
}
void test() {
std::shared_ptr<int> nonConstInt;
func(std::shared_ptr(nonConstInt));
func(std::shared_ptr(std::make_shared<int>()));
}
std::shared_ptr is certainly less wordy than std::const_pointer_cast<SomeType>.
This should not have any performance impact, but sure modifying the call sites is a pain.
Otherwise there's no solution that doesn't involve modifying the called function declarations - but the modification should be acceptable, I think, since it's not any more wordy than what you had already:
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)
{
}
In general, how would you request vector<T> as a type in a template?
template<?vector<T>?>
void allocate() {
vector<T> vector;
}
To be more specific (the example above is a bit dumb), I'm using a service_locator<T> from this library and all of its members are static. The constructor and the destructor of that struct are deleted, therefore there is no such thing as an instance of a service_locator<T>. I'd like to write a function that does something, given some service_locator<T>. Something like the following.
template<?service_locator<T>?> // What do I write here?
T& assertedRef() {
assert(!service_locator<T>::empty(), "Value not initialized for that type!");
return service_locator<T>::ref();
}
My current workaround is to ask for T instead of service_locator<T>. This is a hassle for the callers because it's on them to figure out the underlying type the service_locator is operating on (most of them are aliased to something more readable).
You can use partial specialization for this. Since that's not supported for functions, you have to forward the call to a partially specialized class instance.
template <typename T>
struct AssertedRefFunctor {
static_assert(sizeof(T) == -1, "Not a service locator");
};
template <typename T>
struct AssertedRefFunctor<entt::service_locator<T>> {
T& operator()() {
if (entt::service_locator<T>::empty()) {
throw std::runtime_error("Value not initialized for that type!");
}
return entt::service_locator<T>::ref();
}
};
template <typename T>
auto assertedRef() -> decltype(std::declval<AssertedRefFunctor<T>>()()) {
return AssertedRefFunctor<T>{}();
}
Usage:
assertedRef<entt::service_locator<int>>();
Demo: https://godbolt.org/z/f-oUpY
C++ (at least C++17) does not support template parameter constraints like C# does (they were proposed for C++0x as "concepts" but withdrawn). But you can have a static assert using std::is_base_of.
And as a hint to whoever consumes your library, I'd name the template parameter as TVector and TVectorItem to be clear that it expects a vector<TVectorItem> derivative.
(And I agree with the (now deleted) comment that the Service Locator pattern is an anti-pattern. There are almost always better alternatives to using the Service Locator pattern - but we'd need to know more about your application before suggesting anything).
(And if you're using Visual Studio or MSBuild and want to build a statically-evaluated or constexpr DI container builder, consider using T4 for codegen instead of C++ templates).
#include <type_traits>
template<typename TVector, typename TVectorItem>
void allocate() {
static_assert( std::is_base_of<vector<TVectorItem>, TVector>::value, "TVector must derive from vector<TVectorItem>." );
TVector vector;
}
There are no direct way to do that.
In your case, you might do:
template <typename ServiceLocatorT>
auto assertedRef() -> decltype(ServiceLocatorT::ref())
{
assert(!ServiceLocatorT::empty(), "Value not initialized for that type!");
return ServiceLocatorT::ref();
}
In generic case, template classes might have aliases of there template (as done with STL std::vector<T>::value_type) that you can use.
At worst, you might create type_traits:
template <typename T> struct value_type;
template <typename T> struct value_type<std::vector<T>>
{
using type = T;
};
// ...
I want to change default_deleter for std::unique_ptr. This is quite easy to achieve, but there is one inconvenient - I have to declare variables using 2 template parameters instead of one, something like this:
std::unique_ptr<MyType, MyDeleter<MyType>> myVar;
As you may see the declaration is long and I have a feeling I can use a shorter version, but I don't know how:)
Is it possible to declare some kind of MyUniquePtr<T> that will be the same as std::unique_ptr<T, MyDeleter<T>>?
EDIT: Matthieu M. already answered, but unfortunately I can't use this feature in Visual Studio as it's not implemented. Is there other way to have this behavior?
Actually it is, using template aliases:
template <typename T>
using MyUniquePtr = std::unique_ptr<T, MyDeleter<T>>;
If your compiler doesn't do template aliases yet, here's the C++03 idiom:
template <typename T>
struct MyUniquePtr {
typedef std::unique_ptr<T, MyDeleter<T> > type;
};
MyUniquePtr<MyType>::type var;
The setup is uglier, but the resulting usage is almost as short: you just need to add ::type.
Does your deleter need to be templated on the type of the object being deleted, or is it sufficient for the function call operator to be templated on the type of object being deleted?
Instead of:
template<typename T>
struct MyDeleter
{
void operator()(T* p) const { /* ... */ }
};
Can you write:
struct MyDeleter
{
template<typename T>
void operator()(T* p) const { /* ... */ }
};
This, of course, depends on what state MyDeleter has to maintain.
Another way conforming to c++03 is a subclass, which unfortunately requires you to reproduce the constructors. (This can be done easier, if your compiler supports variadic template arguments, which may be the case for current MSVC.)
template <class T>
class MyUniquePtr : public std::unique_ptr< T, MyDeleter<T> > {
public:
MyUniquePtr(args...) : std::unique_ptr< T, MyDeleter<T> >(args...) { }
};
You might want to consider writing your own version of a make_unique style function instead specialized for your custom allocation / deletion strategy. This also has the advantage that you can perform any specialized resource acquisition / allocation in an exception safe way. You can then declare your unique_ptr as auto and let type deduction work for you.