First of all, apologies for the horrible title. I was experimenting with the C++20 is_detected
functionality. is_detected
basically takes two template parameters, one is a higher-order type which performs checking and the other one is the type to be checked. I ran into problems in the following scenario:
#include <stdio.h>
#include <type_traits>
// kind of how std::experimental::is_detected is implemented
template <template <typename> typename Checker, typename T, typename = void>
struct is_detected: std::false_type {};
template <template <typename> typename Checker, typename T>
struct is_detected<Checker, T, std::void_t<Checker<T>>>: std::true_type {};
struct Foo {
template <typename T>
using Checker = decltype(std::declval<T>().foo());
template <typename T>
static constexpr void call(T &t) {
t.foo();
}
};
template <typename T>
using GlobalChecker = decltype(std::declval<T>().foo());
template <typename T>
struct Wrapper {
template <typename U>
using LocalChecker = typename T::template Checker<U>;
// ^^^^^^^^ clang and msvc require template keyword
// gcc doesn't require it
template <typename U>
constexpr void conditional_call(U &u) const noexcept {
if constexpr (
is_detected<
typename T::Checker,// !!! COMPILE ERROR !!!
// works for
// GlobalChecker,
// LocalChecker and
// Foo::Checker, though.
std::decay_t<U>>
::value) {
Foo::call(u);
}
else {
puts("fallback");
}
}
};
int main() {
struct {
void foo() {
puts("heyy!");
}
} t;
Wrapper<Foo> w;
w.conditional_call(t); // heyy! (if foo didn't exist, then fallback)
}
If I alias the Checker
in the class scope using the using LocalChecker = typename T::template Checker<U>;
, it works; however, I want to learn if there is another way without using using
. In addition, do I need the template
in this using
definition? Because Clang and GCC disagree on that.
Starting with C++17, you do not need the template
keyword between a ::
and a member template specialization name in certain contexts that can only name a type, including the typename-specifier syntax, which is used in the LocalChecker
alias template. So clang and MSVC are wrong to reject that line without the template
, and possibly haven't yet implemented this change.
C++14 [temp.names]/4:
When the name of a member template specialization appears after
.
or->
in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keywordtemplate
. Otherwise the name is assumed to name a non-template.
was replaced with C++17 [temp.names]/4:
The keyword
template
is said to appear at the top level in a qualified-id if it appears outside of a template-argument-list or decltype-specifier. In a qualified-id of a declarator-id or in a qualified-id formed by a class-head-name or enum-head-name, the keywordtemplate
shall not appear at the top level. In a qualified-id used as the name in a typename-specifier, elaborated-type-specifier, using-declaration, or class-or-decltype, an optional keywordtemplate
appearing at the top level is ignored. In these contexts, a<
token is always assumed to introduce a template-argument-list. In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keywordtemplate
.
Since the rules above only require the keyword template
when a member template specialization is named, not just the member template itself, it would seem plain T::Checker
, in addition to T::template Checker
, ought to work at the part of the example using is_detected
. (We don't want typename
, since we're naming the alias template, not a type which is a specialization of that template.) But it's not clear why adding a template should make a difference in when a compiler does or doesn't need help determining the meaning. The open CWG issue 1478 is related.
In any case, the compilers do seem to like it better with the template
hint: your program with is_detected<T::template Checker,
... compiles successfully on clang++, g++, and msvc: see on godbolt.