我可以写一个C++概念,它需要一个函数模板,必须提供一些枚举值作为模板参数吗?

ig9co6j1  于 2023-03-25  发布在  其他
关注(0)|答案(2)|浏览(110)

我想写一个C++概念,它用一个由枚举值专门化的函数模板来描述一个类型。例如,

enum class categories { 
  t1, 
  t2 
};

struct notifiable_type {
  template<categories category>
  auto notification() {}
};

auto main() -> int {
  notifiable_type().notification<categories::t1>();
}

到目前为止,我试过:

// 1
template<typename notifiable_t, typename notification_t>
concept notifiable = std::is_enum<notification_t>::value && requires(notifiable_t t, const notification_t n) {
  { t.template notification<n>() } -> std::same_as<void>;
};

// 2
template<typename notifiable_t, typename notification_t>
concept notifiable = std::is_enum<notification_t>::value && requires(notifiable_t t) {
  { t.template notification<std::declval<notification_t>()>() } -> std::same_as<void>;
};

然而,这两种方法似乎都没有以预期的方式工作:
1.第一种情况有错误Constraint variable 'n' cannot be used in an evaluated context
1.第二种情况编译后没有任何错误,但notifiable<notifiable_type, categories>不满足。
有谁能为我澄清一下吗?我想写的东西可能吗?

k3bvogb1

k3bvogb11#

您已经非常接近这个了(我继续将std::is_enum<E>::value更改为std::is_enum_v<E>,并且还减少了类型的名称,因为它们非常长并且彼此几乎相同,这让我有点困惑):

template<typename T, typename E>
concept notifiable =
    std::is_enum_v<E>
    && requires(T t, const E n) {
        { t.template notification<n>() } -> std::same_as<void>;
    };

问题是你不能像这样使用n,因为它需要是一个常量,而它不是。所以解决方案是发明一个E类型的 * 变量 *,而是发明一个E类型的 * 值 *。而且,好吧,0是一个很好的值:

template <typename T, typename E>
concept notifiable = std::is_enum_v<E>
    && requires(T t) {
        { t.template notification<E{}>() } -> std::same_as<void>;
    };

因为E是一个枚举,所以我们知道E{}是有效的。不需要declval,它也有与原始方法相同的问题:它不是一个常数,它必须是。
一个类似的解决方案,虽然更冗长,而且通常更奇怪,应该是:

template <typename T, typename E>
concept notifiable = std::is_enum_v<E>
    && requires(T t, std::integral_constant<E, E{}> n) {
        { t.template notification<n>() } -> std::same_as<void>;
    };

现在,这是可行的,因为integral_constant<T, V>是constexpr-可转换为值为VT。这种转换在很多上下文中非常有用,因为它允许以保持常量的方式近似传递常量作为函数参数。
但在这种情况下,它是毫无意义的,所以我只是为了说明它。
请注意,这仍然是有限的,我们只是检查特定的E{0}工作-而不是任何E工作(我们通常不能这样做)。这可能已经足够好了,尽管你可能会得到假阴性,如果你有一些情况,比如T::notification对其E有额外的约束。

de90aj5v

de90aj5v2#

将显式整数值强制转换为枚举通常是不受欢迎的,但这可能是所有罪恶中最小的一种情况。而且,零和任何其他数字一样是强制转换的受害者。
Live Demo

#include <type_traits>
#include <concepts>

template<typename notifiable_t, typename notification_t>
concept notifiable = std::is_enum<notification_t>::value &&
    requires(notifiable_t t) {
    { t.template notification<notification_t{0}>() }
    -> std::same_as<void>;
};

enum class categories {
    t1,
    t2
};

struct notifiable_type {
    template<categories category>
    void notification() {}
};

template<notifiable<categories>> struct t {
};

t<notifiable_type> u;   // OK

struct not_notifiable_type {
};

t<not_notifiable_type> v;  // ERROR

相关问题