将模板参数包扩展为内部模板声明时的预期行为

问题描述 投票:0回答:1

有两种情况:

  1. 您可以使用模板参数包并将其扩展为非类型模板参数的嵌套模板声明:
template<class... Ts>
struct Outer {
    template<typename Ts::type...>
    void inner(){}
};  

我认为这非常简单,因为自 C++11 以来这在技术上是可能的,但编译器之间的结果行为差异很大: 对变量

Outer<std::type_identity<bool>, std::type_identity<int>> outer{};
调用以下成员函数会产生以下结果(Demo - 对
main
中的特定行进行注释以查看编译器输出):

outer.inner<true, 42>();

  • 海湾合作委员会:
    error: wrong number of template arguments (2, should be 1)
  • clang:编译
  • MSVC:编译

outer.inner<1.0, 2.0>();

  • 海湾合作委员会:
    error: wrong number of template arguments (2, should be 1)
  • 叮当:
    candidate template ignored: invalid explicitly-specified argument for template parameter 'Vals'
  • MSVC:
    Failed to specialize function template 'void Outer<std::type_identity<bool>, std::type_identity<int>>::inner(void)'
    (后跟
    ICE

outer.inner<true>();

  • 海湾合作委员会:
    ICE
  • 叮当:
    candidate template ignored: deduced too few arguments for expanded pack 'Vals'; no argument for 2nd expanded parameter in deduced argument pack <true>
  • MSVC:编译

outer.inner<>();

  • GCC:编译
  • 叮当:
    candidate template ignored: deduced too few arguments for expanded pack 'Vals'; no argument for 1st expanded parameter in deduced argument pack <>
  • MSVC:编译

虽然 clang 的行为符合我个人的预期,但编译器之间的差异让我觉得我正在做一些官方不允许的事情(也许是 clang 扩展?),那么官方标准说这里应该发生什么?

  1. 使用 C++20 概念,现在使用约束类型参数将模板参数包扩展为嵌套模板参数列表在语法上是有效的(?)。
template<class, std::size_t> concept At = true; template<std::size_t... Is> struct Outer{ template<At<Is>... Ts> void inner(Ts...){} };
在变量 

Outer<0, 1> outer{};

 上调用以下成员函数会产生以下结果(
Demo - 对 main
 中的特定行进行注释以查看编译器输出):

outer.inner(0, 1);

    GCC:编译
  • clang:编译
  • MSVC:编译

outer.inner(0, 1, 2);

    海湾合作委员会:
  • error: mismatched argument pack lengths while expanding 'At<Ts, Is>'
    
    
  • 叮当:
  • candidate function [with Ts = <int, int>] not viable: requires 2 arguments, but 3 were provided
    
    
  • MSVC:
  • the associated constraints are not satisfied
    
    

outer.inner(0);

    海湾合作委员会:
  • error: mismatched argument pack lengths while expanding 'At<Ts, Is>'
    
    
  • 叮当:
  • candidate template ignored: deduced too few arguments for expanded pack 'Ts'; no argument for 2nd expanded parameter in deduced argument pack <int>
    
    
  • MSVC:
  • the associated constraints are not satisfied
    
    
这里我的猜测是

GCC

MSVC
 是正确的,因为它们的行为似乎与重写的方式相同,在这种情况下 
clang
 也表现得像 
GCC
MSVC
 (
Demo ):

template<std::size_t... Is> struct Outer{ template<class... Ts> requires (At<Ts, Is> && ...) void inner(Ts...){} };
但是

clang

似乎创建了某种固定长度的参数包,它可以实现一些有趣的场景,例如领先包(
Demo):

template<std::size_t... Is> struct Outer{ template<At<Is>... Ts, class U> void inner(Ts..., U){} }; int main() { Outer outer<0, 1>{}.inner(true, 1, 2.0); }

clang

 编译并推导出 
Ts = <bool, int>
U = double

那么问题又来了,根据标准,哪种行为是正确的?

c++ language-lawyer c++20 variadic-templates c++-concepts
1个回答
0
投票
对于第一个示例,该行为已由标准完美指定。根据[temp.param]/2

[...]

typename

 后跟限定 ID 表示非类型 
参数声明 中的类型。 [...]

这意味着

typename Ts::type

 表示类型为 
Ts::type
 的非类型模板参数。它实际上不能是其他任何东西:它不能是名为 
Ts::type
 的类型模板参数,因为您无法声明模板参数具有限定名称。

然后,[temp.param]/17 说

[...] 模板参数包是一个

参数声明,其类型包含一个或多个未扩展的包,是包扩展。 [...]

其中一个例子是

template <class... T> struct value_holder { template <T... Values> struct apply { }; // Values is a non-type template parameter pack }; // and a pack expansion
这清楚地表明了应该发生的事情:

...

T
的声明中扩展了包
apply
。例如,类 
value_holder<bool, int>
 包含一个嵌套类模板 
apply
,它采用一个 
bool
 和一个 
int
 模板参数。

在您的示例中,成员模板

inner

 的模板参数包未命名,但这种差异并不相关:
typename Ts::type...
 只是一个 
参数声明,省略了可选名称。

Clang 行为正确;我不知道为什么其他编译器不这样做。请针对 GCC 提交错误。

在你的第二个例子中,Clang 也是正确的。后来在[temp.param]/17中我们有这样一句话:

带有

type-constraint 且包含未扩展参数包的类型参数包是包扩展。

At<Is>... Ts

 是一个类型参数包,其 
type-constraintAt<Is>
,因此此声明扩展了包 
Is
,从而产生固定长度的类型模板参数列表。 
Ts
表示这个固定长度的列表,然后通过参数声明
Ts...
扩展为长度等于
Is
的函数参数列表。

© www.soinside.com 2019 - 2024. All rights reserved.