提问人:gct 提问时间:11/14/2023 更新时间:11/15/2023 访问量:72
使用 C++ 概念时如何手动消除部分专用化的歧义?
How to manually disambiguate partial specializations when using C++20 concepts?
问:
如果我定义了两个概念,并且每个概念都有一个类的部分专用化,那么很明显,如果我有一个同时满足这两个概念的类型,我就有一个歧义:
#include <cstdio>
template <typename T>
concept HasBar = requires {
typename T::Bar;
};
template <typename T>
concept HasBaz = requires {
typename T::Baz;
};
template <typename T>
struct Foo;
template <typename T> requires(HasBar<T>)
struct Foo<T> {
static void frobnicate() {
fprintf(stderr, "HasBar!\n");
}
};
template <typename T> requires(HasBaz<T>)
struct Foo<T> {
static void frobnicate() {
fprintf(stderr, "HasBaz!\n");
}
};
int main() {
struct MyFoo {
using Bar = int;
using Baz = float;
};
Foo<MyFoo>::frobnicate();
}
收益 率:
<source>:42:15: error: ambiguous partial specializations of 'Foo<MyFoo>'
42 | Foo<MyFoo> foo;
| ^
<source>:23:8: note: partial specialization matches [with T = MyFoo]
23 | struct Foo<T> {
| ^
<source>:30:8: note: partial specialization matches [with T = MyFoo]
30 | struct Foo<T> {
| ^
如果我从中删除类型,那么一切都按计划进行并打印出来Baz
Foo
HasBar!
当专业化像这样模棱两可时,我想手动指定优先级。我以为我可以用标签类型塔来做到这一点,较低的值代表较高的优先级:
#include <cstdio>
template <int N>
struct Priority : Priority<N-1> {};
template <>
struct Priority<0> {};
template <typename T>
concept HasBar = requires {
typename T::Bar;
};
template <typename T>
concept HasBaz = requires {
typename T::Baz;
};
template <typename T, typename Enabled = void>
struct Foo;
template <typename T> requires(HasBar<T>)
struct Foo<T, Priority<1>> {
static void frobnicate() {
fprintf(stderr, "HasBar!\n");
}
};
template <typename T> requires(HasBaz<T>)
struct Foo<T, Priority<0>> {
static void frobnicate() {
fprintf(stderr, "HasBaz!\n");
}
};
int main() {
struct MyFoo {
using Bar = int;
using Baz = float;
};
Foo<MyFoo>::frobnicate();
}
但这似乎只是完全排除了两个部分专业化:
<source>:42:4: error: implicit instantiation of undefined template 'Foo<MyFoo>'
42 | Foo<MyFoo>::frobnicate();
在新的 C++20 概念世界中应该如何做到这一点?
答:
2赞
Ted Lyngmo
11/14/2023
#1
如果在这种情况下应该具有优先权,则可以要求 的专用化 不同时具有:Bar
HasBaz<T>
Bar
template <typename T> requires(HasBaz<T> && !HasBar<T>)
// ^^^^^^^^^^
struct Foo<T> {
static void frobnicate() {
fprintf(stderr, "HasBaz!\n");
}
};
输出:
HasBar!
如果从以下位置删除:Bar
MyFoo
int main() {
struct MyFoo {
//using Bar = int;
using Baz = float;
};
Foo<MyFoo>::frobnicate();
}
输出:
HasBaz!
评论
0赞
gct
11/14/2023
是的,这可能是我在这种情况下所做的,显然,随着概念数量的增加,这并不能一概而论。
0赞
Ted Lyngmo
11/14/2023
@gct 不,如果你有很多很多专业,这可能是真的。
0赞
Jarod42
11/15/2023
#2
不过,您可以将优先级与函数重载一起使用:
template <typename T>
requires(HasBar<T>)
void frobnicate(Priority<1>) { fprintf(stderr, "HasBar!\n"); }
template <typename T>
requires(HasBaz<T>)
void frobnicate(Priority<0>) { fprintf(stderr, "HasBaz!\n"); }
template <typename T>
void frobnicate() { frobnicate<T>(Priority<1>{}); }
评论
0赞
gct
11/15/2023
是的,在这种情况下,我需要它来解析特定类型的类,因为我正在实现一种静态解析类型适配器的方法。
评论