I have a Visual Studio 2008 C++ project with a template class that takes the templated value in the constructor like this:
template< typename A >
struct Foo
{
const A& a_;
Foo( const A& a ) : a_( a ) { };
};
Therefore, I have to construct this class like this:
int myval = 0;
Foo< int > foo( myval );
It seems redundant to have to specify int as a template parameter when it's already specified in the constructor. I'd like some way to use it like this:
Foo foo( myval );
As is, I get the compiler error:
error C2955: 'Foo' : use of class template requires template argument list
Thanks,
PaulH
C++17 fixed this issue, introducing template argument deduction for constructors.
Citing Herb Sutter:
[...] you can write just pair p(2, 4.5); instead of pair<int,double> p(2, 4.5); or auto p = make_pair(2, 4.5);. This is pretty sweet, including that it obsoletes many “make” helpers.
If you name the type at all, it has to be the complete type (i.e., Foo<int> and not just Foo).
A class can have multiple constructors or may have no constructors that have a parameter of the class template's type parameter, so there's no way to make this work for all class templates (in my opinion it would be confusing to have this work sometimes but not all the time).
If you are okay not naming the type at all, you can write a MakeFoo<T>() function template that constructs a Foo<T> and use function template argument deduction:
template <typename A>
Foo<A> MakeFoo(const A& a) { return Foo<A>(a); }
This pattern is commonly used in C++ (see, for example, make_shared).
If you want to store the returned object in a variable, though, that variable still needs to have a type. If you are able to move to a compiler that supports C++0x's auto (like Visual C++ 2010), then you can use that. Otherwise, you can use a third-party solution like BOOST_AUTO or write your own (though that would likely be quite a lot of work (-: ).
With auto, your code would look like:
auto foo(MakeFoo(myval));
Type deduction happens only with function template argument, not with class template argument.
In C++0x, you can do something like this:
template<typename T>
Foo<T> CreateFoo(const T & a)
{
return Foo<T>(a);
}
auto foo = CreateFoo(myval); //No need to write CreateFoo<int>(myval);
Related
I have a problem with inner classes in class templates. I have a template class (say: Matrix<T>), and a subclass (say: Matrix<T>::Row). Now I want to to write a function which operates on instances of the subclass (say: negate(Matrix<T>::Row &)). I tried to declare the function with template<class T> negate(typename Matrix<T>::Row &), but when I try to use it, the compiler tells me that it cannot find a match.
Here's an abstract example:
template<class T>
class A
{
public:
class B
{
};
};
template<class T>
void x(typename A<T>::B &)
{
}
int main()
{
A<int>::B b;
x(b); // doesn't work: Error: Could not find a match
// for x<T>(A<int>::B) needed in main().
x<int>(b); // works fine
}
Why does the compiler does not manage to find x in the first case? Is there a way to modify this that it works (without explicitly specifying the type int)?
(I also have similar problems where x is of the form template<class T, class S> void x(typename A<T>::B &, const S &);, whence I would really like not to be forced to explicitly name all types while doing the call.)
I have tried this with g++ 4.4.3, g++ 4.5.2, and Sun Studio 5.9, all give the same result. Thanks a lot in advance for anything helpful!
How should the compiler be able to deduce this? Imagine the following setup:
struct A { typedef int T; };
struct B { typedef int T; };
template <typename S> void foo(typename S::T);
Now when you say int x; foo(x);, there's no way to match this unambiguously.
The point is that you are not deducing a template parameter from a given class template, but rather just an arbitrary, free-standing type. The fact that that type was defined inside another class is not relevant for that.
That is non-deducible context. That is why the template argument cannot be deduced by the compiler.
Just imagine, you might have specialized A as follows:
template <>
struct A<SomeType>
{
typedef std::map <double, double> B;
};
Now this specialization has a nested type called B which is a typedef of std::map<double,double>.
So how would the compiler deduce the type SomeType, given that A<SomeType>::B is std::map<double, double>?
And in fact, there can be many such specializations, as such:
template <>
struct A<SomeOtherType>
{
typedef std::map <double, double> B;
};
Even this specialization has B as nested type.
Now if I say A<T>::B is std::map<double,double>, then can you say what T is? Is it SomeType? or SomeOtherType?
It would need to deduce the type T for the call to template function x, and template argument deduction is only allowed in a specific set of circumstances:
http://publib.boulder.ibm.com/infocenter/compbgpl/v9v111/index.jsp?topic=/com.ibm.xlcpp9.bg.doc/language_ref/template_argument_deduction.htm
A<T>::B
does not seem to be one of them :/
I'm trying to write a generic factory class with automatic self-registration of types for my application.
In order to allow flexibility, this factory has a variadic template parameter for constructor arguments; i.e. it allows either default constructors or constructors requiring any number of arguments. The parameter names are fairly self-explanatory; the AbstractType is the abstract base class for the factory; the returned object will be a std::shared_ptr of this type.
This generic factory works fine and all of the tests I wrote for it were working perfectly fine, until I tried to create a factory for a specific class hierarchy containing classes (as data members) that do not permit copy construction or assignment. I tried to fix this by using an rvalue reference for the template argument; however, this does not work in the manner that I expected. Specifically, if I define the factory instance to take a constructor parameter of type A&&, this fails with an error telling me that there is no conversion from A to A&&.
In this sample, My_Abstract, Data_Context, and Other_Class are declared elsewhere. As described briefly above, the idea here is that a concrete type CT will have a constructor with the signature:
class CT {
CT(Data_Context&&, Other_Class const&);
/* ... */
};
class My_Abstract; // forward declaration
template <class ConcreteType>
using My_Factory_Registrar =
Factory_Registrar<ConcreteType, My_Abstract, Data_Context &&, Other_Class const&>;
using My_Factory =
Generic_Factory<My_Abstract, Data_Context &&, Other_Class const&>;
Perhaps I am missing something fundamental here, but when I revise the code to be:
template <class ConcreteType>
using My_Factory_Registrar =
Factory_Registrar<ConcreteType, My_Abstract, Data_Context const&, Other_Class const&>;
using My_Factory =
Generic_Factory<ConcreteType, Data_Context const&, Other_Class const&>;
Then everything compiles and works correctly. I am well aware that an r-value can be used for a const reference parameter, so I am not confused as to why this worked, as much as I am completely confused why the first code snippet did not work. It almost appears like the rvalue reference qualifier was removed in the process of variadic template expansion.
I'm not sure whether or not it will help at all in clarifying thing, but the code for the factory class itself is as follows:
template <class AbstractType, class...ConstructorArgs>
class Generic_Factory{
public:
static std::shared_ptr<AbstractType> Construct(std::string key, ConstructorArgs... arguments){
auto it = Get_Registry()->find(key);
if (it == Get_Registry()->cend())
return nullptr;
auto constructor = it->second;
return constructor(arguments...);
}
using Constructor_t = std::function<std::shared_ptr<AbstractType>(ConstructorArgs...)>;
using Registry_t = std::map< std::string, Constructor_t>;
Generic_Factory(Generic_Factory const&) = delete;
Generic_Factory& operator=(Generic_Factory const&) = delete;
protected:
Generic_Factory(){}
static Registry_t* Get_Registry();
private:
static Registry_t* _registry_;
};
template <class ConcreteType, class AbstractType, class...ConstructorArgs>
struct Factory_Registrar : private Generic_Factory<AbstractType, ConstructorArgs...>{
using Factory = Generic_Factory<AbstractType, ConstructorArgs...>;
using Constructor_t = typename Factory::Constructor_t;
public:
Factory_Registrar(std::string const& designator, Constructor_t object_constructor){
auto registry = Factory::Get_Registry();
if (registry->find(designator) == registry->cend())
registry->insert(std::make_pair(designator, object_constructor));
}
};
Thanks for your help.
Shmuel
Perfect Forwarding is intended to be used in these cases. Your code is quite long. I use a simplified version of make_unique for demonstration.
template <typename T, typename ...Args>
auto make_unique(Args&&... args) -> std::unique_ptr<T>
{
return std::unique_ptr<T>{new T(std::forward<Args>(args)...)};
}
To be able to forward rvalue input arguments without losing type information, you need to use universal references. The prototypical example is :
template<class T>
void Forwarder(T&& t)
{
Func(std::forward<T>(t));
}
This way, there is no loss of type information and the right overload of Func gets called.
On the other hand, if the body of Forwarder called Func(t), only the lvalue overload of Func would match.
I often find the need to use the following pattern:
template<typename T>
class SomeClass : public Base {
SomeClass(const T& t) {...}
...
};
template<typename T>
SomeClass<T>* makeSomeClass(const T& t) {
return new SomeClass<T>(t);
}
And to use it:
Base* = makeSomeClass(123);
This is useful when I don't want to explicitly specify T because it is a very complex (function types, etc') and the function argument can deduce the type implicitly.
Is there a way to do this without the extra "make" function? Why does template deduction work only in function arguments and not in constructor arguments?
No, there is no way to do this without an extra make function.
The reason it doesn't work with constructors is because it would be ridiculously complicated. Consider this:
template <typename T>
struct Foo
{
Foo(const T& val) { ... }
Foo(const Foo<T>& other) { ... } // Copy constructor
};
Foo<int> x;
What if I then call:
Foo(x);
Does that give me a Foo< Foo<int> > or am I calling the copy constructor for a Foo<int>?
It would be ambiguous in too many places, so the extra function is necessary.
Note that you can automate the create of make functions a little by using template templates:
template <template <typename> class TemplateClass, typename Type>
TemplateClass<Type> make(const Type& x)
{
return TemplateClass<Type>(x);
}
Then you can use:
make<SomeClass>(123); // returns a SomeClass<int>
It does work in constructor arguments, if the constructor is itself a template. The difference is that when you use your helper, you're using a function template, where the compiler can deduce the type. Without the helper, you're using a class template, where the compiler would have to somehow deduce the type before calling the (non-template) constructor.
Is there a way without the extra function? No there isn't. Why the deduction works only for functions? Because with functions you actually provide arguments. If it were allowed for classes, then the only way to deduce the template parameters would be in case when a constructor was called for initialization, which makes lots of additional rules and exceptions and complicates things,
suppose I have a lot of classes with their "*Pack" counterparts in naming. For example, if I have a class Moo, I have MooPack, if I have Foo, I also have FooPack.
I want to have a c++ templated function which returns a FooPack from a Foo
template <class X, class XPack>
XPack packify(X input){
...
}
Is it possible to do this without having to specify the template argument? At the moment, this has to be done like the following:
Moo moo;
MooPack mooppey = packify<Moo, MooPack>(moo);
If it only required the Moo template argument, that ugly template specification bit could go away, but apart from using #defines, which isn't really the best solution either, still doesn't do it.
Is there a way, or will I have to wait for c++0x?
You don't have to specify Moo, just MooPack, because moo will deduce the argument for you. However, I'd suggest that you make MooPack a typedef or nested class (called Pack) inside Moo itself, in which case you can easily access it by typename X::Pack inside the template.
class Moo {
public:
class Pack {
...
};
};
template<typename T> typename T::Pack packify(T t) {
...
}
// usage
Moo m;
Moo::Pack p = packify(m);
As the answer by DeadMG already mentioned you don't need to explicitely specify the parameter type as it can be deduced automaticaly (if it's the second instead of the first template parameter). Since you said you can't change the type declarations to form a link between the classes, I would propose the traits route for that (think std::iterator_traits):
template<typename T> struct pack_traits;
template<> struct pack_traits<Moo> { typedef MooPack Pack; };
...//traits for other packifable classes
template<typename T> pack_traits<T>::Pack packify(T val){...}
...
Moo moo;
MooPack mooppey = packify(moo);
This way you can call the function without manually specifying template arguments, without needing to modify the classes themselves.
I wish to have a non-template class with a template constructor with no arguments.
As far as I understand, it's impossible to have it (because it would conflict with the default constructor - am I right?), and the workaround is the following:
class A{
template <typename U> A(U* dummy) {
// Do something
}
};
Maybe there is a better alternative for this (or a better workaround)?
There is no way to explicitly specify the template arguments when calling a constructor template, so they have to be deduced through argument deduction. This is because if you say:
Foo<int> f = Foo<int>();
The <int> is the template argument list for the type Foo, not for its constructor. There's nowhere for the constructor template's argument list to go.
Even with your workaround you still have to pass an argument in order to call that constructor template. It's not at all clear what you are trying to achieve.
You could use a templated factory function instead of a constructor:
class Foo
{
public:
template <class T> static Foo* create() // could also return by value, or a smart pointer
{
return new Foo(...);
}
...
};
As far as I understand, it's impossible to have it (because it would conflict with the default constructor - am I right?)
You are wrong. It doesn't conflict in any way. You just can't call it ever.
template<class...>struct types{using type=types;};
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
the above helpers let you work with types as values.
class A {
template<class T>
A( tag<T> );
};
the tag<T> type is a variable with no state besides the type it caries. You can use this to pass a pure-type value into a template function and have the type be deduced by the template function:
auto a = A(tag<int>{});
You can pass in more than one type:
class A {
template<class T, class U, class V>
A( types<T,U,V> );
};
auto a = A(types<int,double,std::string>{});
Some points:
If you declare any
constructor(including a templated
one), the compiler will refrain from
declaring a default constructor.
Unless you declare a copy-constructor (for class X one
that takes X or X& or X const
&) the compiler will generate the
default copy-constructor.
If you provide a template constructor for class X which takes
T const & or T or T& then the
compiler will nevertheless generate a
default non-templated
copy-constructor, even though you may think that it shouldn't because when T = X the declaration matches the copy-constructor declaration.
In the latter case you may want to provide a non-templated copy-constructor along with the templated one. They will not conflict. When X is passed the nontemplated will be called. Otherwise the templated
HTH
You could do this:
class C
{
public:
template <typename T> C(T*);
};
template <typename T> T* UseType()
{
static_cast<T*>(nullptr);
}
Then to create an object of type C using int as the template parameter to the constructor:
C obj(UseType<int>());
Since you can't pass template parameters to a constructor, this solution essentially converts the template parameter to a regular parameter. Using the UseType<T>() function when calling the constructor makes it clear to someone looking at the code that the purpose of that parameter is to tell the constructor what type to use.
One use case for this would be if the constructor creates a derived class object and assigns it to a member variable that is a base class pointer. (The constructor needs to know which derived class to use, but the class itself doesn't need to be templated since the same base class pointer type is always used.)
Here's a workaround.
Make a template subclass B of A. Do the template-argument-independent part of the construction in A's constructor. Do the template-argument-dependent part in B's constructor.
It is perhaps easier and more intuitive to rely on std::in_place_type_t<T> which is used in std::variant, std::any, etc for exactly the same purpose:
#include <utility>
class A {
template <typename U>
A(std::in_place_type_t<U>) {
// Do something
}
};
A a(std::in_place_type_t<MyType>{});
try doing something like
template<class T, int i> class A{
A(){
A(this)
}
A( A<int, 1>* a){
//do something
}
A( A<float, 1>* a){
//do something
}
.
.
.
};
Just simple to add a dummy variable like
class A {
template<typename T>
A(const T&, int arg1, int arg2);
}