在两个本身就是模板的模板类型参数之间强制执行通用模板类型参数

Enforcing a common template type parameter among two template type parameters that are themselves templates

提问人:js87 提问时间:11/5/2023 最后编辑:cigienjs87 更新时间:11/5/2023 访问量:50

问:

在 C++ 中,有没有办法确保两个或多个模板类型参数本身就是具有通用模板类型参数的模板类型?

假设我有这个:

struct ArbitraryType {};

template <typename T>
class ArbitraryTemplateClass1 {
  /* ... */
};

template <typename T>
class ArbitraryTemplateClass2 {
  /* ... */
};

我想要一个可以接受 和 作为模板类型参数的模板,但要确保它们都有一个通用的模板类型参数(然后对该类型做一些事情)。例如,我希望能够执行以下操作:ArbitraryTemplateClass1ArbitraryTemplateClass2TT

template <typename U <typename T>, typename V<T>> // desired (but incorrect) syntax
struct CommonTemplateTypeParameterProducer {
  T Produce() { return T(); }
};

// Which would allow this:
int main() {
  CommonTemplateTypeParameterProducer<ArbitraryTemplateClass1<ArbitraryType>, 
                                      ArbitraryTemplateClass2<ArbitraryType>> producer;

  // Type of t should be ArbitraryType
  auto t = producer.Produce();

  // This should fail to instantiate CommonTemplateTypeParameterProducer because U and V don't share a common T
  //CommonTemplateTypeParameterProducer<ArbitraryTemplateClass1<ArbitraryType>, 
  //                                    ArbitraryTemplateClass2<int>>         producer2; 

}

是否有任何机制允许这种行为,而无需直接了解任意模板类和任意类型?(例如添加一个 typedef 来指定 的类型 不会是一个选项)。如果可能的话,我还想避免添加第三个模板类型参数,理想情况下,我希望模板能够推断出它正在强制执行的通用类型。ArbitraryTemplateClass1TCommonTemplateTypeParameterProducer;

注意:像这样的东西也有助于确保两个模板类型参数(也是可变参数模板)都具有相同的模板参数。

我已经研究了C++的概念/约束/要求和模板模板参数,但到目前为止,似乎没有一个能为这个特定问题提供解决方案。

C++ 类型参数 模板

评论

1赞 463035818_is_not_an_ai 11/5/2023
ArbitraryTemplateClass1<ArbitraryType>不是一个模板,而是一个类型。
0赞 js87 11/5/2023
@463035818_is_not_an_ai明白了,我不确定如何清楚地表达出来。您将如何描述一个类型,该类型也是模板的实例化,另一个类型作为其模板参数?
0赞 463035818_is_not_an_ai 11/5/2023
这不是关于措辞你的问题。只是指出代码中的错误。您的模板将通过 ie 实例化 第一个参数是模板而不是类型(在第二个参数中否定了这个问题)CommonTemplateTypeParameterProducer<ArbitraryTemplateClass1, ArbitraryTemplateClass2<ArbitraryType>>
0赞 js87 11/5/2023
@463035818_is_not_an_ai 我的代码具有 ArbitraryTemplateClass1<ArbitraryType>而不是单独的 ArbitraryTemplateClass1 并没有错误。如果是后者,我关于强制执行通用模板类型参数的问题将没有任何意义。
0赞 463035818_is_not_an_ai 11/5/2023
是的,对不起,我完全误读了代码

答:

1赞 HolyBlackCat 11/5/2023 #1

这是可能的,但可能不是最好的设计,因为如果有人需要添加另一个模板参数,一切都会中断。或者,如果您决定制作一个未模板化的版本,并且仅适用于一种特定类型。ArbitraryTemplateClass1

最好添加类似 / 的内容,并在模板中检查它,而不是实际的模板参数。 不应对其模板参数的模板参数进行监管。using type = T;ArbitraryTemplateClass12CommonTemplateTypeParameterProducer

#include <concepts>

template <typename T>
struct A
{
    using type = T;
};

template <typename T>
struct B
{
    using type = T;
};

template <typename X, typename Y>
requires std::same_as<typename X::type, typename Y::type>
struct Foo
{
    X x;
    Y y;
};

int main()
{
    Foo<A<int>, B<int>> foo;
}

这是另一种选择。它与你提出的设计有同样的问题,但至少你不需要拼写两次模板参数:

template <typename T>
struct A {};

template <typename T>
struct B {};

template <
    template <typename> typename X,
    template <typename> typename Y,
    typename T
>
struct Foo
{
    X<T> x;
    Y<T> y;
};

int main()
{
    Foo<A, B, int> foo;
}

最后,这正是你所要求的。我添加了一个帮助程序模板,用于从任意模板中提取模板参数。这同样存在我上面描述的问题,并且还需要拼写两次模板参数。

#include <concepts>

template <typename T>
struct GetTemplateArgument {};
template <template <typename> typename T, typename U>
struct GetTemplateArgument<T<U>> {using type = U;};

template <typename T>
struct A {};

template <typename T>
struct B {};

template <typename X, typename Y>
requires std::same_as<typename GetTemplateArgument<X>::type, typename GetTemplateArgument<Y>::type>
struct Foo
{
    X x;
    Y y;
};

int main()
{
    Foo<A<int>, B<int>> foo;
}

评论

0赞 js87 11/5/2023
谢谢,最后一个正是我一直在寻找的那种东西!我正在寻找您的第一个解决方案之外的东西的主要原因是因为我想以一种有效的方式编写 Foo,即使我无法直接修改 A 或 B。我也不想要像第二个这样的东西,因为我可能已经知道也可能不知道 A 和 B 的常见类型,就像 Foo 在另一个模板中一样,我所拥有的只是两种类型,只有当它们具有共同的模板参数时才应该起作用。
0赞 HolyBlackCat 11/5/2023
@js87嗯,我明白了。那么是的,也许这是有道理的。这取决于你希望它的可定制性,以及你是否希望用户像将来那样编写更多的类。如果这是我的代码,我可能会看看 的实现,也许有一种方法可以推断模板参数,即使他们没有添加 typedef。ArbitraryTemplateClass1ArbitraryTemplateClass1
0赞 cigien 11/5/2023 #2

您可以使用符合要求的部分专业化来执行此操作。

使用采用 2 个类型参数的主类。不要定义它,因此如果您尝试使用它,则会出现错误:

template <typename, typename>
struct CommonTemplateTypeParameterProducer;

然后编写一个部分专用化,它接受 2 个模板参数和 ,以及 1 个类型参数。如果变量声明中使用的类型与模式匹配,并且将选择此专用化:ABTA<T>B<T>

template <template <typename> typename A,
          template <typename> typename B,
          typename T>
struct CommonTemplateTypeParameterProducer<A<T>, B<T>> 
{
  T Produce() { return T(); }
};

这是一个演示