gpt4 book ai didi

c++ - C++ 标准的哪一部分阻止显式指定此模板的参数?

转载 作者:行者123 更新时间:2023-12-04 11:16:25 28 4
gpt4 key购买 nike

在我的 C++ 旅行中,我遇到了以下习语(例如 Abseil 中的 here)以确保模板化函数不能显式指定模板参数,因此它们不是有保证的 API 的一部分,可以自由地改变而不破坏任何人:

template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);
它似乎确实有效:
foo.cc:5:3: error: no matching function for call to 'AcceptSomeReference'
AcceptSomeReference<char>('a');
^~~~~~~~~~~~~~~~~~~~~~~~~
foo.cc:2:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'ExplicitArgumentBarrier'
我在直观的层面上理解为什么会这样,但我想知道如何使它精确。 标准的哪一部分保证无法为该模板显式指定模板参数?
我对 clang 在这里出色的错误信息感到惊讶;就像它识别成语一样。

最佳答案

构造形式的主要原因

 template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(T const&);
禁止您为 T 指定模板参数明确是对于 跟随模板参数包的所有模板参数 要么是编译器 必须能够从函数参数中推导出相应的参数 (或者他们必须有一个默认参数) [temp.param]/14 :

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]). A template parameter of a deduction guide template ([temp.deduct.guide]) that does not have a default argument shall be deducible from the parameter-type-list of the deduction guide template.


正如@DavisHerring 所指出的,仅此一段并不一定意味着必须扣除。但是: 寻找匹配的模板参数分几个步骤执行:首先将考虑明确指定的模板参数列表 [temp.deduct.general]/2 ,只有稍后才会执行模板类型推导,最后才考虑默认参数 [temp.deduct.general]/5 . [temp.arg.explicit]/7在这种情况下的状态

Note 3: Template parameters do not participate in template argument deduction if they are explicitly specified


这意味着模板参数是否可以推导不仅取决于函数模板声明,还取决于应用模板参数推导之前的步骤,例如考虑明确指定的模板参数列表。不能显式指定模板参数包后面的模板参数,因为它不会参与模板参数推导([temp.arg.explicit]/7),因此也不会被推导(这将违反 [temp.arg.explicit]/7)。参数]/14)。
因此,当显式指定模板参数时,编译器会将它们“贪婪地”匹配到第一个参数包(至于以下参数,默认值应该可用,或者它们应该可以从函数参数中扣除!反直觉地,即使是这种情况,如果模板参数是 type and non-type parameters !)。所以没有办法完全明确地指定带有签名的函数的模板参数列表
template <typename... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);
如果它被调用
AcceptSomeReference<int,void,int>(8.0);
所有类型都将归因于模板参数包,而不会归因于 T .所以在上面的例子中 ExplicitArgumentBarrier = {int,void,int}T将从参数中扣除 8.0double .

更糟的是你可以 使用引用作为模板参数
template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);
为这样的屏障明确指定模板参数并非不可能,但 引用模板参数非常严格并且不太可能偶然发生,因为他们必须尊重 [temp.arg.nontype]/2 ( constexpr 用于非类型)和 [temp.arg.nontype]/3 (对引用的限制甚至更多!):您需要某种 static variable with the correct cv -qualifier (例如,以下示例中的 static constexpr int xstatic int const x 也不起作用!)就像下面的代码片段( Try it here! ):
template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T& t) {
std::cout << t << std::endl;
return;
}

static int x = 93;
int main() {
AcceptSomeReference<x>(29.1);
return EXIT_SUCCESS;
}
这样,通过添加这个不太可能的模板参数的可变参数模板作为第一个模板参数,您可以阻止某人完全指定任何模板参数。无论用户尝试放置什么,都很可能无法编译。没有明确的模板参数列表 ExplicitArgumentBarrier将自动扣除长度为零 [temp.arg.explicit]/4/Note1 .

对@DavisHerring 的附加评论
允许某人在可变参数模板后明确指定模板参数会导致歧义并破坏现有规则。考虑以下函数:
template <typename... Ts, typename U>
void func(U u);
调用中的模板参数是什么 func<double>(8.0)是? Ts = {}, U = doubleTs = {double}, U = double ?解决这种歧义的唯一方法是允许用户仅显式指定所有模板参数(而不仅仅是一些)。但这又导致了默认参数的问题:
template <typename... Ts, typename U, typename V = int>
void func2(U u);
调用 func2<double,double>(8.0)再次模棱两可( Ts = {}, U = double, V = doubleTs = {double}, U = double, V = int )。现在,您必须在此上下文中禁止默认模板参数以消除这种歧义!

关于c++ - C++ 标准的哪一部分阻止显式指定此模板的参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68157960/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com