I ran into a compile error in some complex, C++ template code, which I've simplified as follows:
struct MyOptions
{
static const size_t maxArray = 2;
static const uint maxIdx = 8;
};
class OtherClass
{
uint num;
public:
OtherClass(uint val) : num(val)
{
}
void OtherCall(const char *varName, uint arraySize)
{
std::cout << '#' << num << ": " << varName << '[' << arraySize << ']' << std::endl;
}
template <class OPTS_> inline void OtherMethod(const char *varName)
{
OtherCall(varName, OPTS_::maxIdx);
}
};
template <size_t COUNT_> class ConstArray
{
OtherClass *other[COUNT_];
public:
ConstArray(OtherClass *o1, OtherClass *o2) // Just sample logic, shouldn't hard-code 2 elements
{
other[0] = o1;
other[1] = o2;
}
inline OtherClass *operator[](size_t idx) const
{
return other[idx]; // Array itself not changeable by caller
}
};
template <class OPTS_> class MyClass
{
ConstArray<OPTS_::maxArray> others1;
ConstArray<2> others2;
public:
MyClass(OtherClass *o1, OtherClass *o2) : others1(o1, o2), others2(o1, o2)
{ // Just test code to initialize the ConstArray<> members
}
inline void PrintInfo(uint idx, const char *varName)
{
OtherClass *other1Ptr = others1[idx];
other1Ptr->OtherMethod<OPTS_>(varName); // This works
others1[idx]->OtherMethod<OPTS_>(varName); // This FAILS!!
others2[idx]->OtherMethod<OPTS_>(varName); // This works
}
};
int main(int argc, char *argv[])
{
OtherClass a(9), b(42);
MyClass<MyOptions> mine(&a, &b);
mine.PrintInfo(1, "foo");
return 0;
}
The error message in g++ 5.4.0 for the "This FAILS!!" line, above, was
error: expected primary-expression before ‘>’ token
others1[idx]->OtherMethod<OPTS_>(varName); // This FAILS!!
^
And yet, obviously when I used the temporary other1Ptr = others1[idx]
, the same logic compiled just fine split into 2 statements, which was leading me to believe it was a g++ bug.
But I used an online compiler to try it in Clang, and got different (and conflicting) errors:
error: use 'template' keyword to treat 'OtherMethod' as a dependent template name
others1[idx]->OtherMethod<OPTS_>(varName); // This fails
^
template
error: use 'template' keyword to treat 'OtherMethod' as a dependent template name
others2[idx]->OtherMethod<OPTS_>(varName); // This works
^
template
2 errors generated.
So Clang tells me what's actually wrong with the others1[idx]->OtherMethods<>()
line, and additionally informs me that the others2[idx]->OtherMethod<>()
line that worked in g++ is actually wrong!
Sure enough, if I change the PrintInfo() code, it compiles fine in Clang:
inline void PrintInfo(uint idx, const char *varName)
{
OtherClass *other1Ptr = others1[idx];
other1Ptr->OtherMethod<OPTS_>(varName); // This works
// others1[idx]->OtherMethod<OPTS_>(varName); // This FAILS!!
others1[idx]->template OtherMethod<OPTS_>(varName); // This works
// others2[idx]->OtherMethod<OPTS_>(varName); // This works ONLY IN g++!
others2[idx]->template OtherMethod<OPTS_>(varName); // This works
}
And this code also compiles fine in g++, so it seems it is the correct behavior.
Yet as we already saw, g++ also accepted
others2[idx]->OtherMethod<OPTS_>(varName); // This works ONLY IN g++!
So is that a bug in g++? Or is Clang being too strict for this logic? And is the workaround, splitting the others1[idx]->OtherMethod<>()
line into two pieces (with a temporary variable) actually correct, or should it be using the "template" keyword somehow also?