0

In the gem5 codebase, there is a class that follows this pattern (the following code is a minimal example that recreates the issue in question):

template <size_t Size>
class VecRegContainer;

template <typename VecElem, size_t NumElems, bool Const>
class VecRegT
{
    static constexpr size_t Size = sizeof(VecElem) * NumElems;
  public:
    using Container = typename std::conditional<Const,
                                                const VecRegContainer<Size>,
                                                VecRegContainer<Size>>::type;
  private:
    Container& container;
    
  public:
    const VecElem& operator[](size_t idx) const
    {
        return container.template raw_ptr<VecElem>()[idx];
    }
};

template <size_t Size>
class VecRegContainer
{
    std::array<uint8_t, Size> container;
  public:
    template <typename Ret>
    const Ret* raw_ptr() const { return (const Ret*)container.data(); }
};

Now, this compiles just fine. However, my confusion is this: why do we need the template keyword in container.template raw_ptr<VecElem>()[idx];? I read this stack overflow answer but I don't see how it maps to my example.

In the function, we know that VecElem is a type, so it should be clear that raw_ptr is a template member function of the object container. However, if I remove the template keyword, the code fails to compile with the following error:

error: use 'template' keyword to treat 'raw_ptr' as a dependent template name
        return container.raw_ptr<VecElem>()[idx];
                         ^
                         template 
1 error generated.

What exactly is raw_ptr "dependent" on? Isn't it clear from context?

rcplusplus
  • 2,767
  • 5
  • 29
  • 43
  • 3
    The compiler has no way to know that `container.raw_ptr` is in fact meant to be a template, and that `<` that follows it is not a less-than operator. It needs a hint. – Igor Tandetnik Jul 25 '20 at 04:11
  • @IgorTandetnik but `VecElem` is a type right? which means that the `<` and `>` together must make up a template specialization. If I had some global function `template T foo()`, then something like `foo()` would work just as well with no need for the `template` keyword. What separates the two cases? – rcplusplus Jul 25 '20 at 04:16
  • 1
    Another answer goes more into how a template can be specialized. `Foo::x` depends on any specializations that pop up, even ones that come after this piece of code but before the instantiation. It's entirely legal for `Foo::x` to make sense in the primary template and not some specializations or vice-versa, unfortunate as that turns out to be when you'd like specializations to offer the same interface (_cough_ `vector`). – chris Jul 25 '20 at 04:19
  • 1
    What separates the two cases is that `raw_ptr` is used in a context-sensitive way. It depends on what `Container` is, and that is not known at the time the template is parsed, only later when it is instantiated. You have to tell the compiler so it can parse the template. Templates are not macros. – Raymond Chen Jul 25 '20 at 06:06
  • Yes, `VecElem` is a type. But how far ahead do you expect the compiler to look? Suppose it were `raw_ptr` - now `VecElem(0)` could be an expression to be compared with, or it could be a non-type template argument. So now you have to look further to see if `>` could be a greater-than operator. For the compiler to resolve this syntactic ambiguity automatically, it needs to perform an infinite lookahead - to compile the rest of the translation unit both ways and see which way works. – Igor Tandetnik Jul 25 '20 at 13:07
  • The C++ standard serves two communities; both are represented on the standardization committee. It strives to make the language convenient for programmers that use it - but at the same time it needs to be actually implementable by compiler and library authors. It wouldn't make anyone any good to standardize a feature that the compiler vendors can't implement. – Igor Tandetnik Jul 25 '20 at 13:09
  • @RaymondChen But `Container` was declared above as a `std::conditional` which means it can only be one of two things. I guess my main concern is, how could I tell before compiling that I need the `template` keyword? – rcplusplus Jul 25 '20 at 18:55
  • @IgorTandetnik in your second example with `VecElem(0)` I could see there being ambiguity, but such an ambiguity shouldn't exist for the code as-is right? – rcplusplus Jul 25 '20 at 18:57
  • 1
    Right. But the language rules don't separate simple cases from complex ones. The standard says that a dependent name is to be presumed to refer to a non-template unless `template` keyword is specified. Otherwise, the standard would have to exhaustively document all cases where the keyword may be omitted; that'd just add unnecessary complexity, both in understanding and teaching the language, and in implementing it. – Igor Tandetnik Jul 25 '20 at 19:17
  • To the compiler, `std::conditional` is a class like any other. It doesn't have any built-in knowledge of its semantics, and can't infer that `std::conditional::type` is either `A` or `B`. It's not even necessarily true - you could provide a specialization of `std::conditional` for your types that does something different. – Igor Tandetnik Jul 25 '20 at 19:24
  • In any case, even knowing that `Container` is `VecRegContainer` doesn't tell anything about `VecRegContainer::raw_ptr`. For one thing, the definition of `VecRegContainer` hasn't been seen yet by the time `raw_ptr` is used; only the forward declaration has been. And even if you do provide the definition first, there could be a specialization of `VecRegContainer` for some `Size`, provided later, that defines `raw_ptr` differently. – Igor Tandetnik Jul 25 '20 at 19:25

0 Answers0