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.
Related
I have a class template that inherits the constructors of the base class template. (As for c++20) Is there a way to deduce the template arguments of the derived class from the constructor arguments of base?
If I specify the type explicitly, that works. Or if I reimplement the constructor and call the constructor of base, that also would work but is there a way to do without that?
template<typename T>
struct CTestBase
{
using Type = T;
CTestBase() = default;
CTestBase(T t){}
};
template<typename T>
struct CTestDer : public CTestBase<T>
{
using CTestBase<T>::CTestBase;
};
void test()
{
//CTestDer der(int{}); //ERROR
CTestDer<int> der(int{}); //OK
}
You can add a user-defined deduction guide:
#include <utility>
template<typename T>
struct CTestBase
{
using Type = T;
CTestBase() = default;
CTestBase(T t){}
};
template<typename T>
struct CTestDer : public CTestBase<T>
{
using CTestBase<T>::CTestBase;
};
template<typename T>
CTestDer(T &&t) -> CTestDer<std::remove_cvref_t<T>>;
void test()
{
CTestDer der(int{}); // OK now.
}
With a little bit more work it should be possible to:
Use a variadic template in the deduction guide
Have the deduction guide use decltype to construct, using its own deduction guide, the superclass
Use a specialization to figure out the superclass's template parameters
Use that to construct the subclass, to deduce it
This should handle anything. But this will be a lot of work. For this simple use case, and if it's not expected that the superclass will change much, this would be overkill.
but is there a way to do without that?
Yes, just add user-defined deduction guides for CTestDer:
template<typename T>
CTestDer(T) -> CTestDer<T>;
Demo
Is it possible for a class method to be called somehow and automatically deduce templated arguments?
I'm trying to create an object with a create class method that calls a private constructor.
I'm doing so in order to make sure the created instance is allocated on the heap.
The class in templated and I'd like the user not having to know the types (can be complicated with rvalue reference to lambdas).
Here's a toy example of what I'd like to achieve (I know the constructor is public here):
template <typename T>
class hello
{
public:
hello(T t){}
static std::shared_ptr<hello<T>> create(T t)
{
return std::make_shared<hello<T>>(t);
}
};
template <typename T>
auto create_hello(T t)
{
return std::make_shared<hello<T>>(t);
}
void test_hello()
{
auto h1 = hello(3); // ok
auto h2 = std::make_shared<hello<int>>(3); // ok
auto h3 = create_hello(3); // ok
auto h4 = hello<int>::create(3); // ok
auto h5 = hello::create(3); // Compile error: 'hello' is not a class, namespace, or enumeration
}
Is there a way to invoke the static create method and having the template parameters automatically deduced like when calling the constructor directly?
No, this is not currently possible. There are only a few contexts in which the template arguments of a class can be deduced:
any declaration that specifies initialization of a variable and variable template, e.g.
hello h(3);
new-expressions, e.g.
auto h = new hello{3};
function-style cast expressions, e.g.
auto h = hello(3);
and from c++20:
the type of a non-type template parameter
In this expression:
hello::create(3)
the template argument needs to be deduced, since it's not specified, but none of the above contexts apply, since there is no rule for deduction of a template parameter from the invocation of a static member function.
You are mixing up two different things:
Constructor template deduction and
function template deduction.
Whereas auto h1 = hello(3) works since, it makes use of constructor template deduction, but since create is not a constructor, the template parameter cannot be deduced from the parameter.
To solve that, you have to source out your create function:
template <class U>
static std::shared_ptr<hello<T>> make_hello(U t)
{
return std::make_shared<hello<U>>(t);
}
Edit:
If you want have the constructor private, just friend the function:
template <typename T>
class hello
{
hello(T t){}
public:
template <class U>
friend std::shared_ptr<hello<U>> make_hello(U t);
};
template <class T>
std::shared_ptr<hello<T>> make_hello(T t)
{
return std::shared_ptr<hello<T>>(new hello<T>(t));
}
I am trying to write a class template that uses a parameter-pack and implements a member function for each type contained in the parameter-pack.
This is what I have so far:
template <typename...T>
class Myclass {
public:
void doSomething((Some_Operator_to_divorce?) T) {
/*
* Do Something
*/
std::cout << "I did something" << std::endl;
}
};
My goal is to have a class template that can be used in the following way:
Myclass<std::string, int, double> M;
M.doSomething("I am a String");
M.doSomething(1234);
M.doSomething(0.1234);
Where the class template mechanism will create an implementation for a doSomething(std::string x), a doSomething(int x) and a doSomething(double x) member function but not a doSomething(std::string x, int i, double f) member function.
I found a lot of examples in the web on the usability of parameter-packs, but I could not figure out if it can be used for my purpose, or if I totally misunderstood for what a parameter-pack can be used.
I thought that I need to unpack the parameter-pack but, after reading a lot of examples about unpacking parameter packs, I believe that this is not the right choice and it has a complete different meaning.
So, therefore, I am looking for a operation to "divorce" a parameter-pack.
There is no "operator" specifically that supports this, but what you're requesting can be done in a few different ways, depending on your requirements.
The only way to "extract" T types from a parameter pack of a class template with the purpose of implementing an overload-set of functions is to implement it using recursive inheritance, where each instance extracts one "T" type and implements the function, passing the rest on to the next implementation.
Something like:
// Extract first 'T', pass on 'Rest' to next type
template <typename T, typename...Rest>
class MyClassImpl : public MyClassImpl<Rest...>
{
public:
void doSomething(const T&) { ... }
using MyClassImpl<Rest...>::doSomething;
};
template <typename T>
class MyClassImpl<T> // end-case, no more 'Rest'
{
public:
void doSomething(const T&) { ... }
};
template <typename...Types>
class MyClass : public MyClassImpl<Types...>
{
public:
using MyClassImpl<Types...>::doSomething;
...
};
This will instantiate sizeof...(Types) class templates, where each one defines an overload for each T type.
This ensures that you get overload semantics -- such that passing an int can call a long overload, or will be ambiguous if there are two competing conversions.
However, if this is not necessary, then it'd be easier to enable the function with SFINAE using enable_if and a condition.
For exact comparisons, you could create an is_one_of trait that only ensures this exists if T is exactly one of the types. In C++17, this could be done with std::disjunction and std::is_same:
#include <type_traits>
// A trait to check that T is one of 'Types...'
template <typename T, typename...Types>
struct is_one_of : std::disjunction<std::is_same<T,Types>...>{};
Alternatively, you may want this to only work if it may work with convertible types -- which you might do something like:
template <typename T, typename...Types>
struct is_convertible_to_one_of : std::disjunction<std::is_convertible<T,Types>...>{};
The difference between the two is that if you passed a string literal to a MyClass<std::string>, it will work with the second option since it's convertible, but not the first option since it's exact. The deduced T type from the template will also be different, with the former being exactly one of Types..., and the latter being convertible (again, T may be const char*, but Types... may only contain std::string)
To work this together into your MyClass template, you just need to enable the condition with SFINAE using enable_if:
template <typename...Types>
class MyClass
{
public:
// only instantiates if 'T' is exactly one of 'Types...'
template <typename T, typename = std::enable_if_t<is_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
// or
// only instantiate if T is convertible to one of 'Types...'
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
};
Which solution works for you depends entirely on your requirements (overload semantics, exact calling convension, or conversion calling convension)
Edit: if you really wanted to get complex, you can also merge the two approaches... Make a type trait to determine what type would be called from an overload, and use this to construct a function template of a specific underlying type.
This is similar to how variant needs to be implemented, since it has a U constructor that considers all types as an overload set:
// create an overload set of all functions, and return a unique index for
// each return type
template <std::size_t I, typename...Types>
struct overload_set_impl;
template <std::size_t I, typename T0, typename...Types>
struct overload_set_impl<I,T0,Types...>
: overload_set_impl<I+1,Types...>
{
using overload_set_impl<I+1,Types...>::operator();
std::integral_constant<std::size_t,I> operator()(T0);
};
template <typename...Types>
struct overload_set : overload_set_impl<0,Types...> {};
// get the index that would be returned from invoking all overloads with a T
template <typename T, typename...Types>
struct index_of_overload : decltype(std::declval<overload_set<Types...>>()(std::declval<T>())){};
// Get the element from the above test
template <typename T, typename...Types>
struct constructible_overload
: std::tuple_element<index_of_overload<T, Types...>::value, std::tuple<Types...>>{};
template <typename T, typename...Types>
using constructible_overload_t
= typename constructible_overload<T, Types...>::type;
And then use this with the second approach of having a function template:
template <typename...Types>
class MyClass {
public:
// still accept any type that is convertible
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T& v)
{
// converts to the specific overloaded type, and call it
using type = constructible_overload_t<T, Types...>;
doSomethingImpl<type>(v);
}
private:
template <typename T>
void doSomethingImpl(const T&) { ... }
This last approach does it two-phase; it uses the first SFINAE condition to ensure it can be converted, and then determines the appropriate type to treat it as and delegates it to the real (private) implementation.
This is much more complex, but can achieve the overload-like semantics without actually requiring recursive implementation in the type creating it.
I wrote a function template to "convert"/repack a boost::shared_ptr<T> to a std::shared_ptr<T> and vice versa by following this proposal. It's working fine unless I have a boost::shared_pt<T> and the type of T is an abstract class.
What I figured out so far is, that the problem occurs when boost/shared_ptr.hpp and boost/shared_array.hpp are included together. If only boost/shared_ptr.hpp is included it's working when the type of T is an abstract class.
I'm using clang 3.3 and boost 1.55.0 . It would be great if someone could tell my why it's not working and how to get it working.
Thanks for your help
Here is a minimal example:
//main.cpp
#include <boost/shared_array.hpp> //removing this include and it's working
#include <boost/shared_ptr.hpp>
#include <memory>
template<typename SharedPointer> struct Holder {
SharedPointer p;
Holder(const SharedPointer &p) : p(p) {}
Holder(const Holder &other) : p(other.p) {}
Holder(Holder &&other) : p(std::move(other.p)) {}
void operator () (...) const {}
};
template<class T>
std::shared_ptr<T> to_std_ptr(const boost::shared_ptr<T> &p)
{
typedef Holder<std::shared_ptr<T>> H;
if(H *h = boost::get_deleter<H, T>(p)) // get_deleter seems to cause the problem
{
return h->p;
}
else
{
return std::shared_ptr<T>(p.get(), Holder<boost::shared_ptr<T>>(p));
}
}
Here the code I used to test it:
//main.cpp
template<typename T> class Base
{
public:
T value;
virtual void abstract() = 0;
virtual ~Base() {}
};
template<typename T> class Derived : public Base<T>
{
public:
virtual void abstract() override {}
virtual ~Derived() {}
};
int main(int argc, const char * argv[])
{
boost::shared_ptr<Base<int>> ptr{new Derived<int>()};
// error here
std::shared_ptr<Base<int>> a = to_std_ptr(ptr);
// no error here
std::shared_ptr<Base<int>> b = to_std_ptr(boost::static_pointer_cast<Derived<int>>(ptr));
return 0;
}
Here's the error message(shortened):
boost/smart_ptr/shared_array.hpp:111:102: error: array of abstract class type 'Base<int>'
shared_array( shared_array<Y> const & r, typename boost::detail::sp_enable_if_convertible< Y[], T[] >::type = boost::detail::sp_empty() )
main.cpp:64:40: note: in instantiation of template class 'boost::shared_array<Base<int> >' requested here
if(H *h = boost::get_deleter<H, T>(p))
main.cpp:86:36: note: in instantiation of function template specialization 'to_std_ptr<Base<int> >'requested here
std::shared_ptr<Base<int>> i = to_std_ptr(ptr);
main.cpp:23:18: note: unimplemented pure virtual method 'abstract' in 'Base'
virtual void abstract() = 0;
What I get from the error message is that the compiler tried to create an array of abstract classes what of course doesn't work. But why is he even trying to do so and what has boost/sharred_array to do with that. Is he maybe picking the wrong overload for boost::get_deleter?
Here's a small example of where the error comes from:
struct abstract
{
virtual void foo() = 0;
};
template<class X, class Y>
struct templ {};
template<class T>
struct bar
{
template<class U>
bar(templ<U[], T[]>) {} // (A)
};
int main()
{
bar<abstract> x;
}
It seems even to be illegal to form the type array of [abstract-type] in (A), therefore instantiating the class template bar with the argument abstract makes the program ill-formed.
The same thing is happening in the background for shared_array. But why is shared_array<Base> instantiated?
The free function boost::get_deleter is overloaded, shared_array.hpp adds an overload to the overload set (actually, adds a template):
template< class D, class T > D * get_deleter( shared_array<T> const & p );
Before overload resolution, even before finding out which functions are viable, function templates need to be instantiated. Instantiating this get_deleter template above leads to instantiating shared_array<Base>, which leads to the program being ill-formed.
The solution is, not to let the above get instantiated: Don't supply the template parameter T, it can't deduce the T in shared_array<T> from a shared_ptr<T>: the two types are unrelated.
Change
if(H *h = boost::get_deleter<H, T>(p))
to
if(H *h = boost::get_deleter<H>(p))
and it works.
Explanation why letting T be deduced works:
When writing a function call where a function template could be meant (looking at the name called), the template parameters have to be set. You can supply them explicitly (inside <> as in get_deleter<H, T>). If you don't supply all of them explicitly (as in get_deleter<H>), the remaining ones have to be deduced from the arguments of the function call.
After all template parameters have been either set explicitly or deduced, their occurrences in the function template are substituted. The error when using get_deleter<H, Derived> occurs in this step: the substituted get_deleter looks like this:
template<> H * get_deleter( shared_array<Derived> const & p );
Here, shared_array<Derived> needs to be instantiated. But during this instantiation, the error explained above occurs. (Note: it's not in the immediate context of get_deleter, therefore SFINAE doesn't apply.)
Now, when you don't explicitly supply the second template parameter, it has to be deduced. And this deduction fails for the function template
template< class D, class T > D * get_deleter( shared_array<T> const & p );
if you use an argument expression of type shared_ptr<Derived>. As deduction fails, no instantiation takes place of the function template, and therefore no instantiation of shared_array<Derived> (deduction fails = some template parameters could not be set).
Why does deduction fail? The compiler needs to deduce the template parameter T inside the function parameter type shared_array<T> const& from the argument expression, which is of the type shared_ptr<Derived>. But this deduction can only succeed (with few exceptions) when the function parameter type can be made equal to the argument expression type. I.e., it can only succeed if there's some type X, such that shared_array<X> is the same type as shared_ptr<Derived>. But there isn't: the specializations of shared_ptr and shared_array are unrelated.
Therefore, the template parameter T of this get_deleter overload cannot be deduced; therefore this function template isn't instantiated, and shared_array<Derived> isn't instantiated.
Deduction failure is a special kind of failure (like SFINAE): It doesn't lead to the program being ill-formed (i.e. it doesn't lead to a compilation error). Rather, the function template for which deduction didn't succeed simply doesn't add a function to the overload set. If there are other functions in the overload set, one of those can be called instead.
Another function template
template<class D, class T> D * get_deleter( shared_ptr<T> const & p )
from boost/smart_ptr/shared_ptr.hpp runs through the same process. However, deduction succeeds here, and a specialization get_deleter<H, T> (with the H and T from to_std_ptr) is added to the overload set. It'll later be chosen by overload resolution.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
using a template class without a template argument
If I have a templated function, I don't need to instantiate it, since it can be inferred from the arguments, like so:
template<typename T> void MyFunc(T arg);
int x;
MyFunc(x);
Is this true for any scenario where the compiler can guess the template parameters? Specifically, I am thinking of this:
template<typename T>
class MyClass {
public:
MyClass(T) { }
};
int x;
MyClass<int> c1(x); // regular style
MyClass c2(x); // is this allowed?
Yes and no.
The compiler doesn't deduce types for class template parameters, but does allow defaults, so if you're using int quite a bit for this template, you could do:
template <typename T=int>
class MyClass {
public:
MyClass(T) {}
};
int x;
MyClass<> c2(x);
Note that this only works for one particular type per template though. It's not choosing the type based on the type of parameter you supply, just using the default you've specified for the template, is you didn't specify a type but passed (say) a double, the template above would still instantiate over int, not double.
Since the compiler can/will deduce template parameters for function templates, you can also create a small template function and use auto:
template <class T>
MyClass<T> make_MyClass(T const &v) {
return MyClass<T>(v);
}
int x;
auto c2 = make_MyClass(x);
No, it isn't allowed, as the compiler can only deduce the types during template function invocation.
That said, the common solution is a helper function.
template<typename T>
class MyClass {
public:
MyClass(T) { }
};
template<typename T>
MyClass<T> makeMyClass(T x)
{
return MyClass<T>(x);
}
No, it isn't allowed; template parameters deduction works only for template functions, template classes don't deduct template parameters from the parameters given to the constructor.