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;
}
- Why do msvc and clang produce different outputs here? Msvc is fine having the "template" omitted for the call with arguments, but not without
- 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.