I'm trying to understand why I need to use a template disambiguator in some openCL code.
EDIT: Here's a minimal reproducing case:
//test.hpp
#include <iostream>
#include <vector>
#include "cl.hpp"
template <typename T>
class Foo {
public:
std::vector<cl::Platform> platforms;
std::vector<cl::Device> devices;
Foo();
void bar();
};
template<typename T>
Foo<T>::Foo() {
cl::Platform::get(&platforms);
platforms[0].getDevices(CL_DEVICE_TYPE_GPU, &devices);
}
template<typename T>
void Foo<T>::bar() {
// Fails to compile: error: expected expression
//std::cout << devices[0].getInfo<CL_DEVICE_NAME>() << std::endl;
// Works
std::cout << devices[0].template getInfo<CL_DEVICE_NAME>() << std::endl;
// Also works
cl::Device &mydevice = devices[0];
std::cout << mydevice.getInfo<CL_DEVICE_NAME>() << std::endl;
}
and the source file:
//test.cpp
#include "test.hpp"
int main() {
Foo<double> foo = Foo<double>();
foo.bar();
return 0;
}
What is different about the reference that makes a template disambiguator unnecessary? I"m trying to understand where the ambiguity is here. (Also, I know I'm not using the templated variable in my class, this is just a minimal case that reproduced the issue.)
First of all, this is just a typical issue in general in template code. The issue is that because, at the point of parsing the nature of getInfo
is unknown (is it a function template or a regular member variable ?) the parser does not know whether to interpret <
as less-than or as the start of specifying template parameters.
Thus the rule in §14.2 [temp.names]/4, of which the example is:
template<class T> void f(T* p) {
T* p1 = p->alloc<200>(); // ill-formed: < means less than
T* p2 = p->template alloc<200>(); // OK: < starts template argument list
T::adjust<100>(); // ill-formed: < means less than
T::template adjust<100>(); // OK: < starts template argument list
}
Now, in your case this seems a bit silly I fathom, because it's a Device
right ? Yes, in your case. But what of:
template <typename T> struct Wrecker { using type = std::vector<Device>; };
template <> struct Wrecker<double> { using type = std::vector<Other>; };
template <typename T>
struct Foo {
using Devices = typename Wrecker<T>::type;
Devices devices;
void bar();
};
So, yes, the rule is not as precise as it could be. It could delve even further into the fray and check whether the actual type can be deduced...
... but do you realize that, as it is, the simple fact of allowing you to elide template
in some circumstances (non-dependent situations) already requires one of the worst compiler hacks ever ? The traditional pipeline: tokenization -> parsing -> semantic analysis has to be completely twisted in C++ to allow feedback of semantic analysis during the parsing phase to automatically disambiguate that foo<4>()
is a template call.
Personally, my recommendation would have been to mandate template
systematically. It would have saved a lot of troubles.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With