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) { ... }
Related
I need to pass a unique pointer to a derived template class to a function that takes a unique base template class, like this:
template <typename T>
class Base {};
template <typename T>
class Derived : public Base<T> {};
template <typename T>
void foo(std::unique_ptr<Base<T>>){}
//or
template <typename T>
class MyClass{
public:
MyClass(std::unique_ptr<Base<T>> arg) : _arg(std::move(arg)) {}
private:
std::unique_ptr<Base<T>> _arg;
};
int main()
{
auto b = make_unique<Derived<int>>();
foo(std::move(b));
MyClass mc(std::move(b))
}
Why is this not working and how can I fix it?
I get an error:
'void foo1<T>(std::unique_ptr<Base<T>,std::default_delete<Base<T>>>)': cannot convert argument 1 from 'std::unique_ptr<Derived<int>,std::default_delete<Derived<int>>>' to 'std::unique_ptr<Base<T>,std::default_delete<Base<T>>>'
but it work
auto derived = std::make_unique<Derived<int>>();
std::unique_ptr<Base<int>> base = std::move(derived);
C++ doesn't deduce template arguments in this situation. You can specify <int>, and that will succeed.
foo<int>(std::move(b)); // fine
MyClass<int> mc(std::move(b)); // fine
See it on coliru
You can't have template argument deduction also consider implicit conversions, at least not in most situations. Normally the argument type must match the parameter type exactly for deduction of a template argument to be possible (in this case to deduce T), but std::unique_ptr<Base<int>> and std::unique_ptr<Dervived<int>> are not the same type.
As the other answer suggests you can explicitly specify the template argument instead of trying to have it be deduced.
If you want to automate this without having to add anything to Derived or Base you can however make use of one of the exceptions to the general rule above. If the template parameter is a reference-to or pointer-to base of the argument type, then it may (with certain conditions) still be used for deduction:
// Here an exception to the deduction rules applies
// and `Base<T>*` can be deduced against a pointer `X*`
// if `X` is (uniquely) derived from a `Base<T>`
template<typename T>
auto as_base_ptr(Base<T>* p){
return p;
}
template<typename X>
auto to_base_unique_ptr(std::unique_ptr<X> p) {
using base_type = std::remove_pointer_t<decltype(as_base_ptr(std::declval<X*>()))>;
return std::unique_ptr<base_type>(std::move(p));
}
template <typename T>
void foo(std::unique_ptr<Base<T>>){
}
template <typename X>
void foo(std::unique_ptr<X> p){
foo(to_base_unqiue_ptr(std::move(p)));
}
But even simpler you can ask yourself whether you really need to have the function foo take std::unique_ptr<Base<T>> specifically (e.g. because you need access to T) or whether std::unique_ptr<X> wouldn't already be enough.
Let's say I've got a templated class, and a function that accepts it in a shared pointer to const, using its template parameter as part of its signature:
template <class T>
class SomeClass {};
// Doesn't need to modify the SomeClass object.
template <class T>
T DoSomething(std::shared_ptr<const SomeClass<T>>);
In this case I can call DoSomething using a shared pointer to non-cost SomeClass by explicitly specifying the template parameter:
DoSomething<int>(std::make_shared<SomeClass<int>>());
But this doesn't work without being explicit, because type deduction fails.
How can I make the function callable with type deduction in this case? Obviously I could write another overload that accepted a shared pointer to non-const, but it's a drag to need to define every function of this type twice.
Ideally this would fail for inputs that aren't correct (shared pointers to other things, or non-shared pointers) at overload resolution time.
One possibility is to use a helper to check for the right type while letting DoSomething accept any T (and fail to compile for the wrong T):
#include <iostream>
#include <type_traits>
#include <iomanip>
#include <memory>
#include <type_traits>
template <typename T>
struct Foo{};
template <typename T>
struct is_right_type : std::false_type {};
template <typename X>
struct is_right_type<std::shared_ptr<const Foo<X>>> : std::true_type {};
template <class T>
auto DoSomething(T t){
static_assert(is_right_type<T>::value);
return 42;
}
int main()
{
//DoSomething(42); // ERROR
std::shared_ptr<const Foo<int>> x;
std::shared_ptr<Foo<int>> y;
DoSomething(x); // OK
DoSomething(y); // ERROR
}
You'll might need to do something extra to get the return type correctly deduced.
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.
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;
// ...
};