在 C++ 中使用默认参数跳过模板参数真的不可能吗,为什么语法另有建议?
Is it really impossible to skip template parameters with default arguments in C++, why does syntax suggest otherwise?
我一直在尝试寻找一种方法来跳过不位于模板参数列表末尾的模板参数,派生的 class 已在其基础 [=28] 中分配了默认值=].
我已经对这个主题进行了一些研究,也在此处进行了研究。虽然在 SO 上讨论了类似的问题 - 许多答案基本上表明它不起作用与非常特殊的情况有关,例如哈希映射案例 here. Also I found this "Potatoswatter" 的回答,在我看来这与不可能的情况相矛盾跳过这样的参数。在他的回答中,他声称这个声明是有效的:
template< class A, class B = int, class C >
class X;
假设模板参数确实不能被跳过(除非在参数列表的末尾),这样的声明将毫无意义。由于 B 被分配了一个默认值,但后面跟着没有默认值的 C,在这种情况下,值 B 将始终必须显式分配,从而使 int 的分配作为 B 的默认值完全无用。上述 X 声明唯一有意义的情况是以下 Y 声明之一有效:
class Y : public X<double, , const std::string&> { ... }
class Y : public X<A = double, C = const std::string&> { ... }
那么在推导一个特化的class时真的不可能跳过一个不在模板参数列表末尾的模板参数吗?
如果不可能,为什么会这样,为什么法律语法显然另有建议(参见上面的 class X 示例)?
如果实际上 不是 不可能,那么如何跳过已分配默认值的模板参数?
相关标准包含在"Template Parameters [temp.param]" (14.1).
本质上,只有在应用默认参数的参数后面没有任何没有默认参数的非包参数时,才可以使用默认参数 ([temp.param]/11)。但是,在 [temp.param]/10:
描述的情况下,您引用的语法可用于声明
The set of default template-arguments available for use is obtained by merging the default arguments from
all prior declarations of the template in the same way default function arguments are (8.3.6). [Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example]
"class" 这个词有点隐藏在你的问题中,所以我想我会提到(即使这不是一个确切的答案)默认值 的模板参数可以 是 "skipped",某种程度上,在对 function 模板的调用中。考虑 this code:
template<class X, class Y = int, class Z>
void foo(X x, Y y, Z z) {
(void)x, (void)y, (void)z;
puts(__PRETTY_FUNCTION__);
}
int main()
{
foo(3.14, {}, 1.45f);
}
- 模板参数
X
推导为 double
(如果有默认值,将不使用)。
- 本次调用无法推导模板参数
Y
,所以使用默认值int
。
- 模板参数
Z
推导为 float
。
由于函数参数列表中 {}
的不可推导性,如今在 STL 中经常为函数模板参数提供默认值。例如参见 [=22=].
您可能希望在 C++17 中对构造函数模板参数推导使用类似的技巧,但我的实验表明不是这种情况。在 class 模板定义中,如果您将带有默认值的模板参数放在列表中比不带默认值的模板参数更早的位置,编译器将生成诊断信息。
template<class X, class Y = int, class Z> // error!
struct Foo {};
我一直在尝试寻找一种方法来跳过不位于模板参数列表末尾的模板参数,派生的 class 已在其基础 [=28] 中分配了默认值=].
我已经对这个主题进行了一些研究,也在此处进行了研究。虽然在 SO 上讨论了类似的问题 - 许多答案基本上表明它不起作用与非常特殊的情况有关,例如哈希映射案例 here. Also I found this "Potatoswatter" 的回答,在我看来这与不可能的情况相矛盾跳过这样的参数。在他的回答中,他声称这个声明是有效的:
template< class A, class B = int, class C >
class X;
假设模板参数确实不能被跳过(除非在参数列表的末尾),这样的声明将毫无意义。由于 B 被分配了一个默认值,但后面跟着没有默认值的 C,在这种情况下,值 B 将始终必须显式分配,从而使 int 的分配作为 B 的默认值完全无用。上述 X 声明唯一有意义的情况是以下 Y 声明之一有效:
class Y : public X<double, , const std::string&> { ... }
class Y : public X<A = double, C = const std::string&> { ... }
那么在推导一个特化的class时真的不可能跳过一个不在模板参数列表末尾的模板参数吗?
如果不可能,为什么会这样,为什么法律语法显然另有建议(参见上面的 class X 示例)?
如果实际上 不是 不可能,那么如何跳过已分配默认值的模板参数?
相关标准包含在"Template Parameters [temp.param]" (14.1).
本质上,只有在应用默认参数的参数后面没有任何没有默认参数的非包参数时,才可以使用默认参数 ([temp.param]/11)。但是,在 [temp.param]/10:
描述的情况下,您引用的语法可用于声明The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are (8.3.6). [Example:
template<class T1, class T2 = int> class A; template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example]
"class" 这个词有点隐藏在你的问题中,所以我想我会提到(即使这不是一个确切的答案)默认值 的模板参数可以 是 "skipped",某种程度上,在对 function 模板的调用中。考虑 this code:
template<class X, class Y = int, class Z>
void foo(X x, Y y, Z z) {
(void)x, (void)y, (void)z;
puts(__PRETTY_FUNCTION__);
}
int main()
{
foo(3.14, {}, 1.45f);
}
- 模板参数
X
推导为double
(如果有默认值,将不使用)。 - 本次调用无法推导模板参数
Y
,所以使用默认值int
。 - 模板参数
Z
推导为float
。
由于函数参数列表中 {}
的不可推导性,如今在 STL 中经常为函数模板参数提供默认值。例如参见 [=22=].
您可能希望在 C++17 中对构造函数模板参数推导使用类似的技巧,但我的实验表明不是这种情况。在 class 模板定义中,如果您将带有默认值的模板参数放在列表中比不带默认值的模板参数更早的位置,编译器将生成诊断信息。
template<class X, class Y = int, class Z> // error!
struct Foo {};