首页 > 解决方案 > 为什么不能将概念传递给模板元函数?

问题描述

考虑 Boost.MP11、Brigand 等库提供的任何常见类型级算法……例如:

template<typename... Args>
struct TypeList;

using my_types = TypeList<int, float, char, float, double>;

constexpr int count = boost::mp11::mp_count_if<my_types, std::is_floating_point>::value;
// this holds:
static_assert(count == 3);

请注意,std::is_floating_point可以定义为:

template<typename T>
struct is_floating_point { constexpr bool value = __compiler_magic(T); };

同样,我们有这个std::floating_point概念

template<typename T>
concept floating_point = requires (T t) { __other_compiler_magic(T); };

可悲的是,尽管有相似之处,但如果不为概念引入手动命名的包装器,似乎没有一种简单的方法可以编写这样的东西:

constexpr int count = boost::mp11::count_if<my_types, std::floating_point>::value;

我的问题是:为什么此时不能传递概念来代替类型?是缺乏标准化,还是这些库可以通过提供更多重载来解决?

看起来每个概念都必须包装在一个模板类型中,该类型只会在其模板参数上调用该概念。

从外部看,概念就像是域是 {set of types} -> bool 的元函数。编译器能够延迟将参数传递给“传统的”基于类型的元函数,例如std::is_floating_point,为什么概念似乎不能发生相同的情况?

标签: c++c++20template-meta-programmingc++-concepts

解决方案


字面上的答案是我们有模板模板参数但没有概念模板参数,因此您不能将概念作为模板参数传递。

另一个字面上的答案是,它从来都不是原始概念提案的一部分,也没有人努力建议它作为扩展(尽管我一直在收集用例)。

必须回答的一件事是依赖概念如何影响包容——因为目前概念的使用从来不是依赖的,所以弄清楚包容很简单(实际上,它仍然一点也不简单,但至少你需要的所有东西都是正确的那里)。但在这样的场景中:

template <template <typename> concept C, typename T> 
    requires C<T>
void foo(T); // #1

template <typename T>
void foo(T); // #2

可能如果#1可行,您想说它是一个更好的候选者,而不是#2因为它仍然受到限制而另一个没有。也许那是微不足道的。但是之后:

template <template <typename> concept C, typename T> 
    requires C<T>
void bar(T); // #3

template <OtherConcept T>
void bar(T); // #4

让我们说#3并且#4都是可行的,是否可以说哪个更好?我们通常说整个过载总是比不同的过载更好 - 但这里可能并非如此。也许这只是模棱两可?

在我看来,这似乎是获得概念模板参数需要回答的主要问题。

另一个问题可能是,我可以写吗foo<convertible_to<int>>(42)convertible_to<int>并不是一个真正的一元概念,但它是一种类型约束,在某些情况下被视为一个,所以我仍然希望它能够工作。


一旦我们有了这样的东西,我相信 Boost.Mp11 会很快获得类似的东西:

template <template <typename...> concept C>
struct mp_quote_c {
    template <typename... T>
    using fn = mp_bool<C<T...>>;
};

这样你就可以写:

constexpr int count = mp_count_if_q<my_types, mp_quote_c<std::floating_point>>::value;

推荐阅读