We can use std::unique_ptr to hold a pointer allocated with malloc which will be freed appropriately.
However, the resulting std::unique_ptr's size will be 2 pointers, one for the pointer to the object and one for the pointer to the deleter function instead of the usual 1 pointer to object and an implicit delete. As one answer points out this can be avoided by writing a custom Unique_ptr that knows the proper deleter function. This function can be made known using a template parameter to support any deleter function like so:
template <class T, void (*Deleter)(T *)>
struct Unique_ptr {
explicit Unique_ptr(T *t, void (*)(T *))
: t{t} {}
~Unique_ptr() {
Deleter(t);
}
//TODO: add code to make it behave like std::unique_ptr
private:
T *t{};
};
template <class T>
void free(T *t) {
std::free(t);
}
char *some_C_function() {
return (char *)malloc(42);
}
int main() {
Unique_ptr<char, free> p(some_C_function(), free); //fine
Unique_ptr q(some_C_function(), free); //should be fine
//with the right
//deduction guide
}
This would be really nice if we could use deduction guides to not have to specify the template parameters. Unfortunately I can't seem to get the syntax right. These attempts fail to compile:
template <class T, auto Deleter>
Unique_ptr(T *, Deleter)->Unique_ptr<T, Deleter>;
template <class T, void (*Deleter)(T *)>
Unique_ptr(T *, void (*Deleter)(T *))->Unique_ptr<T, Deleter>;
Alternatively one could write Unique_ptr<free> q(some_C_function()); in order to manually specify the function template parameter, but that creates issues with deducing T.
What is the correct deduction guide to make Unique_ptr q(some_C_function(), free); or Unique_ptr<free> q(some_C_function()); compile?
Why write your own unique_ptr? Just use std::unique_ptr with a custom delete pointer. With C++17, that's very straightforward:
template <auto Deleter>
struct func_deleter {
template <class T>
void operator()(T* ptr) const { Deleter(ptr); }
};
template <class T, auto D>
using unique_ptr_deleter = std::unique_ptr<T, func_deleter<D>>;
Or, as Yakk suggests, more generally:
template <auto V>
using constant = std::integral_constant<std::decay_t<decltype(V)>, V>;
template <class T, auto D>
using unique_ptr_deleter = std::unique_ptr<T, constant<V>>;
which gets you to:
unique_ptr_deleter<X, free> ptr(some_c_api());
Sure, you have to actually write X, but you have no space overhead. In order to accomplish the same thing with deduction guides, you'd need to wrap the function deleter in order to lift it to a template parameter:
template <class T, auto D>
Unique_ptr(T*, func_deleter<D>) -> Unique_ptr<T, func_deleter<D> >;
Which would be used like:
Unique_ptr ptr(some_c_api(), func_deleter<free>());
I'm not sure that's necessarily better, and you run into all the same problems that led to the standard not having deduction guides for std::unique_ptr (i.e.: differentiating between pointers and arrays). YMMV.
Related
I'm trying to implement a push function for a blocking queue which accepts a universal reference as it's template parameter, but requires that the template argument be the same type as the queue's element type:
template <typename ValueType>
class shared_queue
{
public:
template <typename Arg>
requires std::same_as<Arg, ValueType>
void push(Arg&& arg);
private:
std::deque<ValueType> container_;
};
However, I'm not quite sure how is universal reference deduction supposed to work in this case, or if it works at all for that matter. The following code:
shared_queue<int> sq;
int x{ 5 };
sq.push(x); // won't compile
sq.push(5); // all good
does not compile. The compiler complains that:
I'm pretty sure I'm misunderstanding something but I don't know what.
You need to remove_reference from Arg for same_as to consider the int& to x and int the same type. You may also want to remove const in case you have const int x and pass that as a parameter. Removing both (+ volatile) can be done with std::remove_cvref_t:
template <typename Arg>
requires std::same_as<std::remove_cvref_t<Arg>, ValueType>
void push(Arg&& arg) {
container_.push_back(std::forward<Arg>(arg));
}
Another option would be to allow for any arguments that can be used to construct a ValueType:
template <class... Args>
requires std::constructible_from<ValueType, Args...>
void emplace(Args&&... args) {
container_.emplace(container_.end(), std::forward<Args>(args)...);
}
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 trying to declare a function that checks wether a smart pointer is initialized. I wrote two variants of a function that acts on smart pointers, one template function acting on templates, one acting on a template of a template. The problem is, the latter one is supposed to act on at least std::unique_ptr and std::shared_ptr. The std::unique_ptr one is constructed differently from std::shared_ptr. std::unique_ptr one takes two template arguments (namely object type and deleter) whereas std::shared_ptr one takes only one (namely the object type).
#include <iostream>
#include <memory>
template <typename Smartpointer>
void checkPointerinitialization(const Smartpointer& ptr){
if(!ptr.get())
std::cout<<"smart pointer is not initialized\n"<<std::endl;
}
//template <template <typename, typename> class Smartpointer, typename Object, typename Deleter>
//void checkPointerinitializationWithTemplates(const Smartpointer<Object, Deleter>& ptr){
// if(!ptr.get())
// std::cout<<"smart pointer is not initialized in template function either\n"<<std::endl;
//}
//int main(){
// std::shared_ptr<int> myptr;
// checkPointerinitialization(myptr);
// checkPointerinitializationWithTemplates(myptr);
//
//}
template <template <typename> class Smartpointer, typename Object>
void checkPointerinitializationWithTemplates(const Smartpointer<Object>& ptr){
if(!ptr.get())
std::cout<<"smart pointer is not initialized in template function either\n"<<std::endl;
}
int main(){
std::shared_ptr<int> myptr;
checkPointerinitialization(myptr);
checkPointerinitializationWithTemplates(myptr);
}
My solution was to overload the template function, however if I uncomment the first function, I get a template deduction failed - wrong number of template arguments error message from g++. Is it actually possible to overload the template functions in an appropriate manner?
Instead of taking your argument as an instantiation of a class template with one single template parameter, take your argument as a class template taking at least one template parameter:
template <template <class, class...> class Z,
class Object,
class... Ts>
void foo(const Z<Object, Ts...>& ptr) { ... }
This will match both std::unique_ptr<X> (with Object=X and Ts={std::default_delete<X>}) and std::shared_ptr<X> (with Object=X and Ts={}).
However, this will not match any non-template smart pointers, like:
struct MySpecialSnowflakeSmartPointer { ... };
So your first approach is probably best:
template <class SP>
void foo(SP const& ptr) { ... }
My code looks like this:
#include <iostream>
#include <memory>
using namespace std;
class myClass
{
int a;
};
template <typename T, template<typename ValueType> class SmartPtr> // (1)
void myFunction(const void *pointer, SmartPtr<T>& pointer2)
{
cout<<"with template"<<endl;
}
void myFunction(const void *pointer, unique_ptr<myClass>& pointer2)
{
cout<<"without template "<< *(static_cast<const int*>(pointer))<<endl;
}
int main()
{
int* a = new int(5);
unique_ptr<myClass> myUniquePtr(new myClass);
shared_ptr<myClass> mySharedPtr(new myClass);
myFunction(static_cast<const void*>(a), myUniquePtr); // (2)
myFunction<int, unique_ptr<myClass> >(static_cast<const void*>(a), myUniquePtr); // (3)
myFunction<int, shared_ptr<myClass> >(static_cast<const void*>(a), mySharedPtr); // (4)
delete a;
return 0;
}
Calling 'myFunction' in (2) is OK - it calls 'myFunction' without template. But in (3) and (4) compiler can't deduce template arguments. It generates errors:
no matching function for call to ‘myFunction(const void*, std::unique_ptr<myClass>&)'
and
no matching function for call to ‘myFunction(const void*, std::shared_ptr<myClass>&)'
respectively.
The question is how to change the second argument of template in (1) so I can instantiate template with arbitrary smart pointer? I want to omit ValueType and use T.
There is no need to have two template arguments, and this will cause problems for smart pointers that are using a custom allocator. Just have one template argument: the type of smart pointer.
template <typename T>
void myFunction(const void *pointer, T & pointer2)
{
}
So T is the smart pointer type. If you need access to the type of the managed object (myClass in this case) you can use typename std::pointer_traits<T>::element_type.
template <typename T>
void myFunction(const void *pointer, T & pointer2)
{
typedef typename std::pointer_traits<T>::element_type obj_type;
obj_type & o = *pointer2;
}
(Of course if you are using C++11 you could also just use decltype(*pointer2) or even auto.)
With this approach you can even use raw pointers as T, assuming that none of your logic depends on them being smart pointers.
As a side note, your invocation likely fails because you are supplying int as the T template argument, which doesn't make any sense; you should be using myClass there.
We have template <class T, class D = default_delete<T>> class unique_ptr;
So your function template should look like:
template <template<typename, typename> class SmartPtr, typename T, typename D>
void myFunction(const void *pointer, SmartPtr<T, D>& pointer2)
{
std::cout << "with template" << std::endl;
}
but it may be simpler to use something like in cdhowie's answer.
The reason it is complaining is because you are giving it a type as the second template argument, but it expects a template (because you used template-template syntax.) The following should at least get it past the template expansion step of compilation (warning, not compiled, but you should get the idea)
myFunction<int, unique_ptr>(static_cast<const void*>(a), myUniquePtr); // (3)
myFunction<int, shared_ptr>(static_cast<const void*>(a), mySharedPtr); // (4)
But, as others have said, you probably don't need this. Just let template argument deduction happen instead of spelling it out.
I have a function that currently takes in two template parameters. One is expected to be the smart pointer, and the other is expected to be the object type. For example, SmartPtr<MyObject> as the first template parameter and MyObject as the second template parameter.
template <typename T, typename TObject>
I would like to know whether I can determine the second parameter, MyObject, automatically from the first parameter SmartPtr<MyObject> or not so that my template function is written like this:
template <typename T>
And the type TObject in the original template function is automatically determined from T which is expected to be a smart pointer.
As requested, here is the function declaration and its use:
template <typename T, typename TObject>
T* CreateOrModifyDoc(T* doc, MyHashTable& table)
{
T* ptr = NULL;
if (!table.FindElement(doc->id, ptr))
{
table.AddElement(doc->id, new TObject());
table.FindElement(doc->id, ptr);
}
return ptr;
}
If you know that the first template parameter will be the smart pointer type, why not declare your function with only one parameter and use it as such:
template<typename T>
void WhatIsIt(SmartPtr<T> ptr)
{
printf("It is a: %s" typeid(T).name());
}
If the classes that can serve as the first template parameter can be made to provide a handy typedef by a common name, you can do this:
template <typename T>
class SmartPtr
{
public:
typedef T element_type;
// ...
};
template <typename PtrType, typename ObjType=PtrType::element_type>
void your_function_here(const PtrType& ptr)
{
// ...
}
Did you write SmartPtr? If so, add this to it
typedef T element_type;
All smart pointers I know of support the member ::element_type. For example boost's shared_ptr: http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/shared_ptr.htm#element_type, but also the std smart pointers support this convention.
template <typename T> class SharedPtr {
public:
typedef T element_type;
// ...
};