Home > Software design >  Why do MSVC and Clang produce different outputs for the same function template call?
Why do MSVC and Clang produce different outputs for the same function template call?

Time:08-29

After messing around with concepts I came across something in visual studio that I didn't understand, although I don't know if the issue here is anything to do with concepts specifically. I'm sure there's a reason for this behaviour, but it would be great if someone could explain. There are two parts to this question. For the following snippet:

#include <concepts>
#include <utility>

template <typename PolicyType, typename T, typename... Ts>
concept concept_policy = requires(Ts&&... args) 
{
    { PolicyType::template Create<T>(args...) } -> std::same_as<T*>;
};

struct basic_policy
{
    template <typename T, typename... Ts>
    static T* Create(Ts&&... args)
    {
        return new T { std::forward<Ts>(args)... };
    }
};

struct type_a
{
    int m_val;
};

template <concept_policy<int> TPolicy = basic_policy>
static void DoSomething()
{
    //works on msvc, msvc needs the "template" for no args, but not with?
    {
        type_a* type1 = TPolicy::Create<type_a>(5); //why is this fine without template?
        type_a* type2 = TPolicy::template Create<type_a>(); //why does this require template if the above doesn't?
    }

    // //clang requires both to have "template"
    // {
    //     type_a* type1 = TPolicy::template Create<type_a>(5);
    //     type_a* type2 = TPolicy::template Create<type_a>();
    // }
}

int main()
{

    DoSomething();

    {
        //both versions compile fine without "template"
        basic_policy policy;
        type_a* type1 = basic_policy::Create<type_a>(5);
        type_a* type2 = basic_policy::Create<type_a>(); 
    }

    return 0;
}
  1. Why do msvc and clang produce different outputs here? Msvc is fine having the "template" omitted for the call with arguments, but not without
  2. Using a similar policy design, is there any way around prefixing the Create with "template"? Ideally I'd like to be able to call TPolicy::Create<type>(...);

CodePudding user response:

Clang is correct: the call to TPolicy::Create<type_a> requires the word template because TPolicy is a dependent type.

Specifically, according to the standard, when we have a fragment of the form T::m< where T is a dependent type other the current instantiation, the compiler must assume that < is the less-than operator, not the beginning of a template argument list. If you mean < as a template argument list, then you must prefix m with the keyword template.

This behaviour is specified in [temp.names]/3. A < that doesn't satisfy any of the conditions listed must be interpreted to mean the less-than operator; the compiler cannot use contextual information to determine that it means the beginning of a template argument list.

As for why MSVC sometimes fails to diagnose the violation, I am not sure.

There is no way to make TPolicy::Create<type>(...); just work without the template keyword. If you really hate writing template, you have to restructure your code so that Create is a non-member, sort of like std::get in the standard library (which would have to be invoked in the form .template get<i>() if it were a class member and the object expression were of dependent type). I guess in this case, Create could be a class template that takes the policy class as one of its template arguments, and the type you want to create as another. I have been told that people often do make their templates into non-members for this exact reason (to avoid having to write template). I think that's a big mistake. It's better to write template than to choose a less natural design.

  • Related