Strange behavior with templates

Hi, I’m facing something strange with templates. Actually. it works but i don’t understand why so I’d like to have some insight from developers. My ROOT version is 5.30.00.
I’m developing a class with a template method:

class GGSRootReader {
public:

  GGSRootReader() {}
  ~GGSRootReader() {}

  template<class T>
  T* GetReader() {
    cout << "Template version" << endl;
    return new T;
  }
};

If I load the header in a ROOT session with .L the class bbecomes available, I can instantiate it but cannot use the template method GetReader:

$ root
root [0] .L GGSRootReader.h
root [1] GGSTRootReader r                                                                                                                       
root [3] r.GetReader<int>()
Error: Symbol r is not defined in current scope  (tmpfile):1:                                                                                                                       
Error: Failed to evaluate r.GetReader
Error: Symbol int is not defined in current scope  (tmpfile):1:
(const int)0
*** Interpreter error recovered ***
root [4]

Ok, I knew that this naive use of templates could be problematic, so I didn’t bother. Then I decided to make a test and tried to specialize the template for the “void” type:

class GGSRootReader {

public:

  GGSRootReader() {}

  ~GGSRootReader() {
  }

  template<class T>
  T* GetReader() {
    cout << "Template" << endl;
    return new T;
  }
};

template<>
void* GGSRootReader::GetReader<void>() {
  cout << "Specialization for void type" << endl;
  return NULL;
}

The sepcialized version now works:

$ root
root [0] .L GGSRootReader.h
root [1] GGSRootReader r
root [2] r.GetReader<void>()
Specialization for void type
(void*)0x0
root [3]

At this point I tried again to call the non-specialized version, and I was really surprised to notice that it was working:

$ root
root [0] .L GGSRootReader.h
root [1] GGSRootReader r
root [2] r.GetReader<void>()
Specialization for void type
(void*)0x0
root [3] r.GetReader<int>()
Template
(int*)0x99b0330
root [4] r.GetReader<GGSRootReader>()
Template
(class GGSRootReader*)0x993a4d8
root [5]

So what is happening? It seems that specializing the template for one type (void) fixed also the non-specialized version of the function. Am I terribly wrong in somethig or is this the intended behavior? I didn’t find much information on this kind of template usage from ROOT CLI.
Thanks for the support.

Hi,

This is a weakness in the CINT template instantiation mechanism ; it does not properly know to instantiate the function when you refer to it as GetReader but since the specialization is explicit, it does know about.
The following slight different example works:[code]#include “Riostream.h”

class GGSRootReader {
public:

GGSRootReader() {}
~GGSRootReader() {}

template
T* GetReader(T*) {
cout << “Template version” << endl;
return new T;
}
};[/code]and used as\root [1] GGSRootReader a root [2] a.GetReader((int*)0) Template version (int*)0xd76b20

Cheers,
Philippe.

Thanks Philippe, I understand a bit better now. But it’s still unclear to me why in my example it is necessary to define at least one specialization to make the unspecialized version work…
Anyway, I reverted my code to a more conventional ROOT coding, so it now works as it should and my question is more a sort of curiosity. So don’t waste your time to find the answer, if there isn’t an obvious one.

[quote]But it’s still unclear to me why in my example it is necessary to define at least one specialization to make the unspecialized version work…[/quote]I am guessing that it change the code path being used in the template instantiator in CINT …

Cheers,
Philippe.