2

This is a simplified example from a real issue that came up recently. There's easy workarounds, but I'd like to understand why the error occurs in the first place. Consider the following code:

#include <stdint.h>

struct Data
{
    template <typename T>
    T get_as() const
    {
        return static_cast<T>(value);
    }

    uint64_t value;
};

Data DoAdd(uint64_t input1, uint64_t input2)
{
    return Data{input1+input2};
}

template<typename T>
T DoOperation(T input1, T input2)
{
    return DoAdd(input1, input2).get_as<T>(); //Error, need template keyword

    //return Data{input1+input2}.get_as<T>(); //Alternative 1, inline the operation: OK.

    //Data value = DoAdd(input1, input2); //Alternative 2, break up into 2 steps: OK
    //value.get_as<T>();
}

int main(int argc, const char** argv)
{
    uint32_t one = DoOperation(1ULL, 2ULL);
    return (one > 0);
}

Both GCC and clang warn that the keyword "template" is needed before get_as(), with clang's error being more useful:

<source>:22:34: error: use 'template' keyword to treat 'get_as' as a dependent template name
    return DoAdd(input1, input2).get_as<T>();
                                 ^
                                 template 

However, I'm having trouble figuring out why the first line in DoOperation is considered a dependent template that needs the template keyword, but Alternative 1 and 2 are fine. If DoAdd was a template function, or Data itself was a templated struct, I think I'd see the problem, but since DoAdd is a normal function and not dependent on T, why does the expression DoAdd(input1, input2).get_as<T>() trigger this behavior?

ScottG
  • 773
  • 6
  • 18
  • I took a look at a number of related questions and answers, like this one: https://stackoverflow.com/a/24091502/753174, which says: _The general rules for adding the template qualifier are mostly similar except they typically involve templated member functions (static or otherwise) of a struct/class that is itself templated._ But that doesn't seem to apply here since `Data` is not a templated class, nor is `DoAdd` a templated function. – ScottG Nov 02 '22 at 19:38
  • the return type of `DoAdd(...)` can change by overload. – apple apple Nov 02 '22 at 20:12

1 Answers1

1

The result type of DoAdd does depend on T, it can change by overloads, so you need to tell the compiler it's a template by specify template keyword.


note: the alternatives only work because you tell the compiler it's a Data

template<typename T>
T DoOperation(T input1, T input2)
{
    auto value = DoAdd(input1, input2);
    value.get_as<T>(); // not work
}
apple apple
  • 10,292
  • 2
  • 16
  • 36
  • Ahh, I see now. At the time the compiler sees `DoAdd`, it doesn't know if there's also another overload of `DoAdd` taking, say `uint32_t` values and returning some other type. That also explains why I can cast input1 and intput2 to uint64_t and also avoid the error, because now only the overload of Data that takes 2 `uint64_t` values can be called. – ScottG Nov 02 '22 at 20:36
  • @ScottG more like it doesn't know anything. say you may never instantiate it with `uint64_t` (or some T that overload resolution would pick that). – apple apple Nov 02 '22 at 20:41
  • @ScottG re comment update: yes it's then determined. – apple apple Nov 02 '22 at 20:42
  • @ScottG correction: `uint32_t` may not work as ADL would not found it. but `T` may be other user defined type (and ADL would work). – apple apple Dec 06 '22 at 17:55