c++ - C++:模板参数的模板模板成员作为需要模板模板参数的模板类的参数
问题描述
首先,为这个可怕的标题道歉。我正在试验 C++20is_detected
功能。is_detected
基本上有两个模板参数,一个是执行检查的高阶类型,另一个是要检查的类型。我在以下场景中遇到了问题:
#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)
}
Checker
如果我在类范围内使用别名using LocalChecker = typename T::template Checker<U>;
,它可以工作;但是,我想了解是否有另一种方法而不使用using
. 另外,我需要template
在这个using
定义中吗?因为 Clang 和 GCC 不同意这一点。
解决方案
从 C++17 开始,在某些只能命名类型的上下文(包括别名模板中使用的typename-specifier语法)中,您不需要template
在 a 和成员模板专业化名称之间使用关键字。所以 clang 和 MSVC 在没有 的情况下拒绝该行是错误的,并且可能尚未实施此更改。::
LocalChecker
template
C++14 [temp.names]/4:
当成员模板特化的名称出现在postfix -expression
.
之后或之后,或者在 qualified-id 中的nested - name-specifier之后,并且postfix-expression的对象表达式是类型相关的或nested-name-specifier在qualified-id中指的是依赖类型,但名称不是当前实例化的成员(14.6.2.1),成员模板名称必须以关键字为前缀。否则,该名称被假定为命名非模板。->
template
如果关键字出现在template-argument-list或decltype-specifier之外,则称该关键字出现在qualified-id
template
的顶层。在declarator-id的限定id或由class-head-name或enum-head-name形成的限定 id中,关键字不应出现在顶层。在用作typename-specifier、Explaind-type-specifier、using-declaration或class-or-decltype中的名称的限定 ID中,可选关键字template
template
出现在顶层会被忽略。在这些上下文中,<
始终假定令牌引入了模板参数列表。在所有其他上下文中,当命名未知特化([temp.dep.type])成员的模板特化时,成员模板名称应以关键字为前缀template
。
由于上述规则仅在template
命名成员模板特化时需要关键字,而不仅仅是成员模板本身,因此看起来很简单T::Checker
,此外T::template Checker
,应该在示例部分使用is_detected
. (我们不想要typename
,因为我们正在命名别名模板,而不是该模板的特化类型。)但不清楚为什么添加模板会在编译器需要或不需要时产生影响帮助确定含义。开放的CWG 问题 1478是相关的。
无论如何,编译器似乎更喜欢它的template
提示:你的程序is_detected<T::template Checker,
... 在 clang++、g++ 和 msvc 上成功编译:参见 godbolt。
推荐阅读
- r - “错误:‘devtools’的包或命名空间加载失败”
- vue.js - 如何在 Vue-Plyr 中更改颜色?
- xml - 有没有办法“简化” VSCode 调试器,使其更像 Chrome 的?
- php - 如何制作自定义 PSR-7 ResponseInterface 以减少样板文件?
- postgresql - pgadmin 远程访问 kubernetes 中的 postresql 服务
- wordpress - 重写 URL 子目录
- r - 在 3D 空间中绘制立方体
- d3.js - 基于时间的 C3 StackedBar 图表,x 轴为小时
- mysql - 加载数据 infile 什么都不做
- python - 如何“重置”在使用来自 Flask 应用程序的数据库的 Python 脚本中运行的 db.session?