C++ 标准的哪一部分阻止显式指定此模板的参数? [英] Which part of the C++ standard prevents explicitly specifying this template's arguments?

查看:48
本文介绍了C++ 标准的哪一部分阻止显式指定此模板的参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my C++ travels I've come across the following idiom (for example here in Abseil) for ensuring that a templated function can't have template arguments explicitly specified, so they aren't part of the guaranteed API and are free to change without breaking anybody:

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'

我从直观的层面上理解为什么会这样,但我想知道如何使其精确.标准的哪些部分保证无法为该模板显式指定模板参数?

I understand at an intuitive level why this works, but I'm wondering how to make it precise. What section(s) of the standard guarantee that there is no way to explicitly specify template arguments for this template?

我对这里的 clang 出色的错误信息感到惊讶;就像它识别了成语.

I'm surprised by clang's excellent error message here; it's like it recognizes the idiom.

推荐答案

表单构造的主要原因

 template <int&... ExplicitArgumentBarrier, typename T>
 void AcceptSomeReference(T const&);

禁止您为 T 明确指定模板参数是因为对于所有跟随模板参数包的模板参数,编译器必须能够推导出来自函数参数的相应参数(或者它们必须有一个默认参数)[temp.param]/14:

prohibits you from specifying the template argument for T explicitly is that for all template parameters that follow a template parameter pack either the compiler must be able to deduce the corresponding arguments from the function arguments (or they must have a default argument) [temp.param]/14:

函数模板的模板形参包后面不能跟另一个模板形参,除非该模板形参可以从函数模板的形参类型列表 ([dcl.fct]) 中推导出来或者有一个默认实参([温度扣除]).没有默认参数的演绎指南模板([temp.deduct.guide])的模板参数应从演绎指南模板的参数类型列表中推导出.

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 在此上下文中说明

As pointed out by @DavisHerring this paragraph alone does not necessarily mean that it must be deducted. But: Finding the matching template arguments is performed in several steps: First the explicitly specified template argument list will be considered [temp.deduct.general]/2, only later on the template type deduction will be performed and finally default arguments are considered [temp.deduct.general]/5. [temp.arg.explicit]/7 states in this context

注3:模板参数如果明确指定,则不参与模板参数推导

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).

This means whether a template argument can be deducted or not depends not only on the function template declaration but also on the steps before the template argument deduction is applied such as the consideration of the explicitly specified template argument list. A template arguments following a template parameter pack can't be specified explicitly as then it would not be participating in template argument deduction ([temp.arg.explicit]/7) and therefore would also not be deducted (which would violate [temp.param]/14).

当明确指定模板参数时,编译器将因此贪婪地"匹配它们;到第一个参数包(至于以下参数,默认值应该可用,或者它们应该可以从函数参数中扣除!与直觉相反,如果模板参数是 类型和非类型参数!).所以没有办法完全明确地指定带有签名的函数的模板参数列表

When explicitly specifying the template arguments the compiler will therefore match them "greedily" to the first parameter pack (as for the following arguments either default values should be available or they should be deductible from function arguments! Counter-intuitively this is even the case if the template arguments are type and non-type parameters!). So there is no way of fully explicitly specifying the template argument list of a function with a signature

template <typename... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);

如果它被调用

AcceptSomeReference<int,void,int>(8.0);

所有类型都将归于模板参数包,而不是归于 T.所以在上面的例子中 ExplicitArgumentBarrier = {int,void,int}T 将从参数 8.0 中减去 double.

all the types would be attributed to the template parameter pack and never to T. So in the example above ExplicitArgumentBarrier = {int,void,int} and T will be deducted from the argument 8.0 to double.

更糟糕的是,您可以使用引用作为模板参数

template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);

为这样的屏障明确指定模板参数并非不可能,但引用模板参数非常严格,而且不太可能偶然发生,因为它们必须尊重 [temp.arg.nontype]/2 (constexpr对于非类型)和 [temp.arg.nontype]/3(对引用的限制甚至更多!):您需要某种 static 变量和正确的 cv-qualifier(例如,以下示例中的 static constexpr int xstatic int const x 之类的东西也不起作用!)就像下面的代码片段一样(在这里试试!):

It isn't impossible to explicitly specify template arguments for such a barrier but reference template parameters are very restrictive and it is very unlikely to happen by accident as they have to respect [temp.arg.nontype]/2 (constexpr for non-types) and [temp.arg.nontype]/3 (even more restrictive for references!): You would need some sort of static variable with the correct cv-qualifier (e.g. something like static constexpr int x or static int const x in the following example would not work either!) like in the following code snippet (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.

This way by adding this variadic template of unlikely template arguments as the first template argument you can hold somebody off from specifying any template parameters at all. Whatever the users will try to put will very likely fail compiling. Without an explicit template argument list ExplicitArgumentBarrier will be auto-deducted to have a length of zero [temp.arg.explicit]/4/Note1.

对@DavisHerring 的补充评论

允许某人在可变参数模板后明确指定模板参数会导致歧义并破坏现有规则.考虑以下函数:

Allowing somebody to explicitly specify the template arguments following a variadic template would lead to ambiguities and break existing rules. Consider the following function:

template <typename... Ts, typename U>
void func(U u);

调用 func(8.0) 中的模板参数是什么?Ts = {}, U = double 还是 Ts = {double}, U = double?解决这种歧义的唯一方法是允许用户仅显式指定所有模板参数(而不仅仅是一些).但这又导致了默认参数的问题:

What would the template arguments in the call func<double>(8.0) be? Ts = {}, U = double or Ts = {double}, U = double? The only way around this ambiguity would be to allow the user to only specify all the template arguments explicitly (and not only some). But then this leads again to problems with default arguments:

template <typename... Ts, typename U, typename V = int>
void func2(U u);

func2(8.0) 的调用再次不明确(Ts = {}, U = double, V = doubleTs= {double},U = double,V = int).现在你必须在这种情况下禁止默认模板参数以消除这种歧义!

A call to func2<double,double>(8.0) is again ambiguous (Ts = {}, U = double, V = double or Ts = {double}, U = double, V = int). Now you would have to ban default template arguments in this context to remove this ambiguity!

这篇关于C++ 标准的哪一部分阻止显式指定此模板的参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆