Reputation: 20264
I have two classes:
template <class T1, class T2>
class foo{
//something here
}
class bar{
bar()=delete;
bar(const foo& a): a(a){}
private:
foo a;
}
This code will not work.
But, how can I achieve something like it?
I know that I should tell what is the type of the used foo
. But in real, I do not allow default constructor. So, I can not make an object of bar
without giving it the suitable foo
.. Why I have to tell the compiler what is the T1
and T2
then?
Upvotes: 0
Views: 120
Reputation: 238341
A data member can have exactly one type. foo
is not a type. It's a template. You cannot leave the deduction of the type of the member to the compiler. That's simply not possible in c++. †
You can also not pass a reference to foo
to the constructor because again, foo
is not a type.
What you need is polymorphism.
DragonRock shows how to achieve static polymorphism with templates: Define bar
to be a class template. Then you can parametrize the type of the member using the template parameters. You still need to specify the template parameters explicitly, so they won't be deduced by the call to the constructor. However, you can then write function template that returns an object with a type that is specific instance of bar
with the template parameters depending on the template parameters of the function. The template parameters of the function can be deduced from the parameter that is passed to it.
Walter shows how to achieve dynamic polymorphism with inheritance: Define all foo
instances to have a common base class. Then store a reference or a pointer of that base class type in bar
.
† Why doesn't the standard allow deducing the type of the data member form the call to the constructor? You may ask. Well let's assume that your example is valid c++. Now consider this code:
bar& b = get_me_a_bar();
auto f = b.foo;
What is the type of f
? There is no call to the constructor of bar
in this code, so it can't be deduced from that. Nevertheless, the type of f
must be known. Sure, there must be a call to the constructor in get_me_a_bar
or somewhere else, but the call could be in another compilation unit. It would make compilation slower by O(n^2)
where n
is the number of compilation units, to look for the calls to the constructor. What if the compilation units that call the constructor haven't been compiled yet? This sort of deduction would create dependencies on what order the units are compiled.
TL;DR C++ is a statically typed language. In statically typed languages the type of a variable must be know at compile time, whenever that variable is used. The uses of a member variable don't necessarily coincide with the uses of the constructor.
Upvotes: 3
Reputation: 45424
Your code makes very little sense: it is not clear what your data member bar::foo a
is meant to represent. Depending on what you really want to achieve (which you failed to explain), this could be an object of a specific type foo<T1,T2>
, or it could be an object of any foo<T1,T2>
type. In the former case, you must also make bar
specific, i.e. a class template, as suggested in other answers.
However, if any foo<T1,T2>
is fine, then you must use polymorphism.
class base_foo {
virtual void func() const = 0; // etc.
virtual~base_foo() {}
};
template<typename T1, typename T2>
class foo : public base_foo
{
void func() const override;
};
Now you can refer to any foo<T1,T2>
via its base base_foo
, but you cannot easily hold an actual object in bar
. The way that most closely reflects your initial code is to store a real foo<T1,T2>
on the heap, but access it via a pointer to base_foo
.
class bar
{
std::unique<base_foo> ptr;
public:
template<typename T1,typename T2>
bar(foo<T1,T2> const&obj)
: ptr(new foo<T1,T2>(obj)) {} // calls foo<T1,T2>::foo<T1,T2>(foo<T1,T2> const&)
};
which makes a copy of the original object as in your code.
Upvotes: 1
Reputation: 1235
What you could do is to write your bar
classs as a template class. And as @nwp suggested, you could make a make_bar
function as a helper to avoid expliciting your types when creating a bar
from a foo
, like so :
template <class T1, class T2>
class foo {
//something here
};
template<class T1, class T2>
class bar {
public:
bar()=delete;
bar(const foo<T1,T2>& a): a(a){}
private:
foo<T1, T2> a;
};
template<class T1, class T2>
bar<T1, T2> make_bar(foo<T1,T2> const& f) {
return bar<T1, T2>{f};
}
int main() {
auto myFoo = foo<int, double>{};
auto barintdouble = make_bar(myFoo);
}
Upvotes: 5