使用 C++ 概念时如何手动消除部分专用化的歧义?

How to manually disambiguate partial specializations when using C++20 concepts?

提问人:gct 提问时间:11/14/2023 更新时间:11/15/2023 访问量:72

问:

如果我定义了两个概念,并且每个概念都有一个类的部分专用化,那么很明显,如果我有一个同时满足这两个概念的类型,我就有一个歧义:

#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> {
      |        ^

如果我从中删除类型,那么一切都按计划进行并打印出来BazFooHasBar!

当专业化像这样模棱两可时,我想手动指定优先级。我以为我可以用标签类型塔来做到这一点,较低的值代表较高的优先级:

#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 概念世界中应该如何做到这一点?

C ++20 C++概念

评论


答:

2赞 Ted Lyngmo 11/14/2023 #1

如果在这种情况下应该具有优先权,则可以要求 的专用化 不同时具有:BarHasBaz<T>Bar

template <typename T> requires(HasBaz<T> && !HasBar<T>)
//                                          ^^^^^^^^^^
struct Foo<T> {
    static void frobnicate() {
        fprintf(stderr, "HasBaz!\n");
    }
};

输出:

HasBar!

如果从以下位置删除:BarMyFoo

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
是的,在这种情况下,我需要它来解析特定类型的类,因为我正在实现一种静态解析类型适配器的方法。