I'm just starting my journey into more advanced template code. Consider the following...
template <typename T>
class node_base
{
public:
T member;
node_base() {}
void do_the_thing(node_base& other)
{
std::cout << other.member;
}
};
template <typename ...Args>
class node : public node_base<Args>...
{
public:
node() :node_base<Args>()...{}
};
int main()
{
node<int> int_node;
node<int, double> double_node;
int_node.do_the_thing(double_node);
double_node.do_the_thing(int_node); // ambiguous call
std::cin.ignore();
return 0;
}
Using Visual Studio 2017 15.4.5, I get the following...
error C2385: ambiguous access of 'do_the_thing' note: could be the
'do_the_thing' in base 'node_base' note: or could be the
'do_the_thing' in base 'node_base'
My understanding is that the compiler should be able to deduce the correct function based on the argument, in this case node<int>. Why is this call considered ambiguous? What can I do to clarify the call while still maintaining this template pattern?
It's not about templates. You can reproduce it with the following:
struct A1 { void foo(); };
struct A2 { void foo(); };
struct B : A1, A2 {};
int main()
{
B b;
b.foo();
}
The relevant part of the Standard is
If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.
So, you have two subobjects of types node_base<int> and node_base<double>.
EDIT: To address changes in the question and as DeanSeo deleted his solution, i will post it here:
template <typename T>
class node_base
{
public:
T member = sizeof(T);
node_base() {}
void do_the_thing(node_base& other) {
std::cout << other.member << std::endl;
}
};
template <typename ...Args>
class node : public node_base<Args>...
{
public:
node() :node_base<Args>()...{}
template <class T>
void do_the_thing(node_base<T>& other) {
node_base<T>::do_the_thing(other);
}
};
int main()
{
node<int> int_node;
node<double, int> double_node;
int_node.do_the_thing<int>(double_node);
double_node.do_the_thing(int_node);
double_node.do_the_thing<double>(double_node);
double_node.do_the_thing<int>(double_node);
return 0;
}
Related
I have the following code which is not compiled with gcc, but compiles perfectly with MSVC.
class B;
class A
{
B *_parent;
public:
template <typename T>
void Do(T val)
{
_parent->DoB(val);
}
A(B *parent)
: _parent(parent)
{
}
};
class B : public A
{
public:
B()
: A(nullptr)
{
}
void DoB(int val)
{
cout << val << endl;
}
};
int main() {
B b;
A a(&b);
a.Do(10);
return 0;
}
Compilation error is as follows:
prog.cpp: In member function ‘void A::Do(T)’:
prog.cpp:15:10: error: invalid use of incomplete type ‘class B’
_parent->DoB(val);
^~
prog.cpp:4:7: note: forward declaration of ‘class B’
class B;
^
According to the similar posts, that behaviour of gcc is correct due to paragraph 14.6.9 of the Standard. But that is counterintuitive since templates should be instantiated only where used. And the only usage occurs after all classes have been defined.
My question is what are the workarounds for this issue? Because using the member template is really convenient in this case. Maybe the problem is with the architecture or something else?
You can declare the parts of A that rely on B to be known, but define them later. The declaration then looks like this
class B;
class A
{
B *_parent;
public:
template <typename T> void Do(T val); /* (see below) */
A(B *parent) : _parent(parent) {}
};
then comes the definition of B
class B : public A { /* As before. */ };
and finally you define the missing pieces
template <typename T> void A::Do(T val)
{
_parent->DoB(val); /* Fine, B is known. */
}
As a side note, the original code compiled with just a warning using gcc 8.
You can add extra layer:
class A
{
B *_parent;
private:
template <typename TB, typename T>
static void CallDoB(TB* b, T&& val) {
b->DoB(std::forward<T>(val));
}
public:
template <typename T>
void Do(T val)
{
CallDoB(_parent, val);
}
A(B *parent) : _parent(parent) {}
};
now b-> is dependent of template and is postponed until real instantiation.
Let's say I have a template my_type. I want it to have general functionality, to have a few extra functions when T is not an array and to have others when T is an array.
Let's say I have the following template:
template <typename T>
class my_class<T> {
public:
int f1(); // This function is available for all T
int f2(); // This function is available when T is not an array
int f3(); // This function is available when T is an array
}
So if I try:
my_class<int> c1; my_class<int[3]> c2;
c1.f1(); c2.f1(); // both fine
c1.f2(); c2.f3(); // both fine
c1.f3(); c2.f2(); // both should give a compile error
I am aware std::unique_ptr does this internally. So how does it do it?
Another way, using enable_if. Note also the use of a base class to capture all common behaviour.
#include <type_traits>
template<class T>
struct my_base
{
int f1();
};
template<class T, typename Enable = void>
class my_class;
template<class T>
class my_class<T, std::enable_if_t<std::is_array<T>::value>>
: public my_base<T>
{
public:
int f3(); // This function is available when T is an array
};
template <typename T>
class my_class<T, std::enable_if_t<not std::is_array<T>::value>>
: public my_base<T>
{
public:
int f2(); // This function is available when T is not an array
};
int main()
{
auto a = my_class<int[]>();
a.f1();
// a.f2();
a.f3();
auto na = my_class<int>();
na.f1();
na.f2();
// na.f3();
}
I have figured it out myself. The following code will do the exact thing I have asked for.
template<typename T>
class my_class {
public:
int f1() { return 1; }
int f2() { return 2; }
};
template<typename T>
class my_class<T[]> {
public:
int f1() { return 1; }
int f3() { return 3; }
};
Note that the implementation of the common function (f1) had to be copied. Now is there a way to use a single implementation? (note that it is NOT as simple as a return 1; like in the example code and thus I can't separate functionality into a non-template function)
I want to have a constructor of a non-template class which is templated by a type. Can anyone help here?
class A
{
public:
static int GetId(){ return 5;}
};
class B
{
public:
B(int id){ _id = id;}
template<typename T>
B() {_id = T::GetId();}
template<typename T>
static B* newB() {return new B(T::GetId());}
private:
int _id;
};
void doSome()
{
B* p1 = B::newB<A>(); //works
B* p2 = new B<A>(); //doesn't compile -- ">>B<< is no template"
}
All template parameters of a constructor template must be deducible (or have default arguments), because there is no syntax for explicitly passing template arguments to a constructor (as you've learned).
There are several possible ways around this:
Provide a constructor-like function template. You're already doing this with newB, there's just no need to force dynamic allocation:
template <class T>
B create() { return B(T::GetId()); }
Provide a tag type and parameterise the consturctor by that:
template <class T>
struct Tag {};
class B
{
public:
template <class T>
B(Tag<T>) : _id(T::GetId()) {}
};
//usage:
B b(Tag<A>());
You cannot explicitly specify the constructor template parameter. It must be deductible.
One solution is to use a helper parameter:
template <class T>
struct Type_holder { using Type = T; };
class B {
public:
B(int id) : id{id} {}
template<typename T>
B(Type_holder<T>) : id{T::GetId()} {}
private:
int id;
};
auto foo()
{
B b{Type_holder<A>{}};
}
Also, please use constructor initialization lists. And careful with those dynamic allocations. Don't use it if it's not needed. And when it's needed use smart pointers.
Is it possible to create a class template with a member function definition only if the object created is of a specific type?
I've created a template class I will use for storing either int or doubles, but for doubles I would like to be able to set precision too (objects created with myclass < double> should have this functionality, but for myclass< int> there is no need for that to be present at all).
I know I can use a base class template, and create new classes "myInt", "myDouble" using that and implement the functionality only in the myDouble class, but I think it would be cleaner to define the functionality (both the function and a member variable) for doubles in the class template, if that's possible and preferable?
Let's add an example to show what I want to do:
#include <iostream>
#include <iomanip>
class commonBase{
public:
void setState(int state);
virtual void print() = 0;
private:
int _my_state;
};
template <typename T>
class generalObject : public commonBase {
public:
void value(T value);
void print(){ std::cout << "My value: " << _my_value << std::endl; }
private:
T _my_value;
};
template <typename T>
void generalObject<T>::value(T value){
_my_value = value;
}
// Is there any way do specialize only only whats different from the generalObject template?
// Here I thought I could specialize the case where a generalObject is created of <double>, but
// when I do, nothing is derived from generalObject (or at least not visible as far as I can tell)
template<>
class generalObject<double>{
public:
void setPrecision(int precision){ _my_precision = precision; }
// here I would like a special implementation of print(), which overrides the print() in generalObject
// and instead also prints according to the precision set when the object is of <double> type.
// Row below an example which doesn't work (compiler error, _my_value undefined)
void print(){ std::cout << "My value: " << std::setprecision(_my_precision) << _my_value << std::endl; }
private:
int _my_precision;
};
int main(int argc, char* argv[]){
generalObject<int> o1;
o1.value(1);
o1.print();
o1.setState(1); //inherited from the commonBase
generalObject<double> o2;
o2.setPrecision(2);
o2.value(2); //here value isn't available (compile error)
o2.print();
o2.setState(123); //also isn't available (compile error)
}
Sure.
template <typename T> class Poly;
void set_precision(Poly<double>* self, int a) {};
If you really want dot notation you can then add:
template <typename T> class Poly {
public: void set_precision(int a){::set_precision(this,a);}
...
However I think you should think about what you're trying to accomplish. If MyInt and MyDouble have different fields and different methods and different implementations, they should probably be different classes.
This can be solved using template specialization.
We first define a common template...
template< typename T >
struct myclass
{
// common stuff
};
... and specialize that for double:
template<>
struct myclass<double>
{
int precision = 10;
void setprecision( int p ){ precision = p; }
};
Now the setprecision() method can only be called for myclass<double>. The compiler will complain if we try to call it for anything else, like myclass<int>.
int main()
{
myclass<double> d;
d.setprecision( 42 ); // compiles
myclass<int> i;
i.setprecision( 42 ); // fails to compile, as expected
}
Demo.
The basic way to have a member function of a class template exist only for some template parameters is to create a specialization of the class template for those template parameters.
template<typename T>class X{
// general definition
};
template<>class X<double>{
// double-specific definition
};
The downside of this is that the specialization will need to duplicate anything that is common. One way to address this is to move the common things out to a base class template:
template<typename T>class Xcommon{
// common stuff
};
template<typename T>class X: public Xcommon<T>{
// general definition
};
template<>class X<double>: public Xcommon<double>{
// double-specific definition
};
Alternatively, you can do it the other way: put the common stuff in the derived class, and the extras in the base, and specialize the base:
template<typename T>class Xextras{
// empty by default
};
template<typename T>class X: public Xextras<T>{
// common definition
};
template<>class Xextras<double>{
// double-specific definition
};
Either way can work; which is better depends on the details.
Both these methods work for data members and member functions.
Alternatively, you can use enable_if to mean that member functions are not selected by overload resolution if the template parameter doesn't meet a required condition. This requires that the member function is itself a template.
template<typename T>class X{
template<typename U=T> // make it a template,
std::enable_if<std::is_same_v<U,double>> double_specific_function(){
// do stuff
}
};
I wouldn't recommend this option unless there is no other choice.
If the question is about a member function, then here is one of the ways to do it without class template specialization:
#include <iostream>
#include <type_traits>
template <typename T>
struct Type {
template <typename U = T,
typename = typename std::enable_if<std::is_same<U, double>::value>::type>
void only_for_double() {
std::cout << "a doubling" << std::endl;
}
};
int main() {
Type<int> n;
Type<double> d;
// n.only_for_double(); // does not compile.
d.only_for_double();
}
Example on ideone.com
If you require a data-member presence based on the template parameter, you will have to do some kind of specialization, in which case it is, probably, simpler to put the function into corresponding specialization.
EDIT: After OP made his question more specific
Here is one way to do it without extra class and getting rid of virtual functions. Hope it helps.
#include <iostream>
#include <iomanip>
template <typename T, typename Derived = void>
class commonBase {
public:
void setState(int state) {
_my_state = state;
}
void value(T value) {
_my_value = value;
}
template <typename U = Derived,
typename std::enable_if<std::is_same<U, void>::value,
void * >::type = nullptr>
void print() const {
std::cout << "My value: " << _my_value << std::endl;
}
template <typename U = Derived,
typename std::enable_if<!std::is_same<U, void>::value,
void * >::type = nullptr>
void print() const {
static_cast<Derived const *>(this)->_print();
}
protected:
T _my_value;
int _my_state;
};
template <typename T>
class generalObject : public commonBase<T> {
};
template<>
class generalObject<double> : public commonBase<double, generalObject<double>> {
private:
friend commonBase<double, generalObject<double>>;
void _print() const {
std::cout << "My value: " << std::setprecision(_my_precision) <<
_my_value << std::endl;
}
public:
void setPrecision(int precision){ _my_precision = precision; }
private:
int _my_precision;
};
int main(){
generalObject<int> o1;
o1.value(1);
o1.print();
o1.setState(1);
generalObject<double> o2;
o2.setPrecision(2);
o2.value(1.234);
o2.print();
o2.setState(123);
}
Same code on ideone.com
I have the following simple code:
template <typename T>
struct base
{
std::vector<T> x;
};
template <typename T>
struct derived : base<T>
{
void print()
{
using base<T>::x; // error: base<T> is not a namespace
std::cout << x << std::endl;
}
};
When I compile the code (using GCC-4.7.2) I get the error that you see in the comment above.
I read here: http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Name-lookup.html#Name-lookup
that
using base<T>::x
has to be included in order to bring in the scope of the base class. Any ideas what is wrong? Thank you in advance!
Put the using declaration in the class definition, not in the function body:
template <typename T>
struct derived : base<T>
{
using base<T>::x; // !!
void print()
{
std::cout << x << std::endl;
}
};
(Of course it's your responsibility to make sure that there's actually an operator<< overload for your std::vector, for example by using the pretty printer.)
You can also make it work if you explicitly say that x is a member:
template <typename T>
struct base
{
std::vector<T> x;
base() : x(1) {}
};
template <typename T>
struct derived : base<T>
{
void print()
{
std::cout << this->x[0] << std::endl;
}
};
int main()
{
derived<int> d;
d.print();
}