gpt4 book ai didi

What is a non-deduced context?(什么是非演绎的语境?)

翻译 作者:bug小助手 更新时间:2023-10-26 21:45:39 38 4
gpt4 key购买 nike



I've stumbled over Why is the template argument deduction not working here? recently and the answers can be summed up to "It's a non-deduced context".

我偶然发现,为什么模板参数演绎在这里不起作用?最近,答案可以归结为“这是一个非演绎的背景”。


Specifically, the first one says it's such a thing and then redirects to the standard for "details", while the second one quotes the standard, which is cryptic to say the least.

具体地说,第一个说是这样的事情,然后重定向到“细节”的标准,而第二个引用的标准,至少可以说是神秘的。


Can someone please explain to mere mortals, like myself, what a non-deduced context is, when does it occur, and why does it occur?

有人能向像我这样的凡人解释一下,什么是非演绎的语境,它什么时候发生的,为什么它会发生?


更多回答

Related: C++, template argument can not be deduced

相关:C++,模板参数无法推导

优秀答案推荐

Deduction refers to the process of determining the type of a template parameter from a given argument. It applies to function templates, auto, and a few other cases (e.g. partial specialization). For example, consider:

演绎是指根据给定参数确定模板参数类型的过程。它适用于函数模板、自动和其他一些情况(例如,部分专门化)。例如,请考虑:


template <typename T> void f(std::vector<T>);

Now if you say f(x), where you declared std::vector<int> x;, then T is deduced as int, and you get the specialization f<int>.

现在,如果您说f(X),其中您声明了std::VECTOR x;,那么T将被推导为int,并且您将得到专门化的f


In order for deduction to work, the template parameter type that is to be deduced has to appear in a deducible context. In this example, the function parameter of f is such a deducible context. That is, an argument in the function call expression allows us to determine what the template parameter T should be in order for the call expression to be valid.

要使扣减起作用,要扣减的模板参数类型必须出现在可扣减上下文中。在这个例子中,f的函数参数是这样一个可推断的上下文。也就是说,函数调用表达式中的参数允许我们确定模板参数T应该是什么,才能使调用表达式有效。


However, there are also non-deduced contexts, where no deduction is possible. The canonical example is "a template parameter that appears to the left of a :::

然而,也有非演绎的上下文,其中不可能进行演绎。典型的例子是“出现在a:左侧的模板参数


template <typename> struct Foo;

template <typename T> void g(typename Foo<T>::type);

In this function template, the T in the function parameter list is in a non-deduced context. Thus you cannot say g(x) and deduce T. The reason for this is that there is no "backwards correspondence" between arbitrary types and members Foo<T>::type. For example, you could have specializations:

在此函数模板中,函数参数列表中的T处于非演绎上下文中。因此,您不能说g(X)并推导出T。这是因为在任意类型和成员foo ::type之间没有“向后对应”。例如,您可以拥有专业化认证:


 template <> struct Foo<int>       { using type = double; };
template <> struct Foo<char> { using type = double; };
template <> struct Foo<float> { using type = bool; };
template <> struct Foo<long> { int type = 10; };
template <> struct Foo<unsigned> { };

If you call g(double{}) there are two possible answers for T, and if you call g(int{}) there is no answer. In general, there is no relationship between class template parameters and class members, so you cannot perform any sensible argument deduction.

如果调用g(Double{}),T有两个可能的答案,如果调用g(int{}),则没有答案。通常,类模板参数和类成员之间没有关系,因此不能执行任何合理的参数演绎。




Occasionally it is useful to inhibit argument deduction explicitly. This is for example the case for std::forward. Another example is when you have conversions from Foo<U> to Foo<T>, say, or other conversions (think std::string and char const *). Now suppose you have a free function:

有时,显式地禁止论点演绎是有用的。例如,std::Forward就是这种情况。另一个例子是从foo到foo 的转换,或者其他转换(想想std::string和char const*)。现在假设您有一个免费函数:


template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);

If you call binary_function(t, u), then the deduction may be ambiguous and thus fail. But it is reasonable to deduce only one argument and not deduce the other, thus permitting implicit conversions. Now an explicitly non-deduced context is needed, for example like this:

如果调用BINARY_Function(t,u),则演绎可能是不明确的,因此会失败。但只推导出一个论点而不推导出另一个论点是合理的,这样就允许进行隐式转换。现在需要一个显式的非演绎上下文,例如:


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

template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}

(You may have experienced such deduction problems with something like std::min(1U, 2L).)

(您可能在使用类似std::min(1U,2L)的产品时遇到过这样的推断问题。)


Note: std::type_identity is available in the standard library since C++20.

注意:STD::TYPE_IDENTITY从C++20开始在标准库中可用。



What is a non-deduced context?


A non-deduced context is a context where template arguments cannot be deduced.
It's not always feasible to deduce template parameters from some construct.
For example:

非演绎上下文是指不能演绎模板参数的上下文。从某些构造中推断模板参数并不总是可行的。例如:


template <int N>
void foo(int x = N * N); // called like foo()

It's obviously impossible to deduce the non-type template parameter N from the default argument N * N. Not only would the compiler have to take the square root of N, it's also unclear where it would get the value of N * N in the first place.

很明显,不可能从默认参数N * N推导出非类型模板参数N。编译器不仅必须取N的平方根,而且还不清楚它首先从哪里获得N * N的值。


List of non-deduced contexts


In the following list, assume that

在下面的列表中,假设



  • T is some template type parameter

  • N is some non-type template parameter


template <typename T, int N>

Each heading cites and explains one bullet in [temp.deduct.type] p5.

每个标题都引用并解释了[temp.duct.type]P5中的一个项目符号。


1. Foo<T>::type



The nested-name-specifier of a type that was specified using a qualified-id.



There is no way to deduce T because type is just an alias such as std::vector<T>::size_type. Knowing only the size_type (which is std::size_t in this case), how could we possibly figure out T? We cannot, because the information isn't contained within std::size_t.

没有办法推断T,因为type只是一个别名,如std::VECTOR ::SIZE_TYPE。只知道SIZE_TYPE(在本例中是std::SIZE_t),我们怎么可能计算出T呢?我们不能,因为信息不包含在std::SIZE_t中。


The most common way that developers run into this problem is by trying to deduce the container from an iterator.

开发人员遇到此问题的最常见方式是尝试从迭代器推断容器。


template <typename T>
void foo(typename T::iterator it); // attempt to deduce T from an iterator

int main() {
std::array<int> v;
foo(v.begin()); // error, trying to deduce std::array<int> from int* (probably)
} // which is obviously imposssible

2. decltype(N)



The expression of a decltype-specifier.



template <int N>
void foo(decltype(N * N - N) n);

The expression of decltype can be arbitrarily complex, and a decltype specifier is a type. If N is a non-type template parameter, how could we possibly know the value from just the type (e.g. guess 123 from int)?

Dectype的表达式可以是任意复杂的,而dectype说明符是一种类型。如果N是一个非类型的模板参数,我们怎么可能从类型中知道值(例如,从int中猜测123)?


3. Foo<0 + N>, int(&)[0 + N]



A non-type template argument or an array bound in which a subexpression references a template parameter.



It would be possible to deduce N from just Foo<N> or from an array bound, but when N only appears as a subexpression, it becomes generally impossible. It is theoretically possible for a simple expression such as 0 + N, but this quickly gets out of hand for more complex expressions such as N * N.

可以仅从foo 或从数组边界推导出N,但当N仅作为子表达式出现时,通常就不可能了。从理论上讲,对于像0+N这样的简单表达式是可能的,但对于像N*N这样更复杂的表达式来说,这很快就失控了。


4. void foo(T x = T{})



A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.



If we call such a function like foo(), then deduction would require some circular logic. The type T would be inferred from the default argument T{}, whose type is T, which is inferred from the default argument, ...

如果我们调用像foo()这样的函数,那么演绎将需要一些循环逻辑。类型T将从默认参数T{}中推断出来,其类型为T,这是从默认参数中推断出来的...




Note: this case applies to the motivating example at the start of the answer.

注:这种情况适用于答案开头的激励例子。


5. void foo(T* function_pointer) with overload sets



A function parameter for which the associated argument is an overload set ([over.over]), and one or more of the following apply:



  • more than one function matches the function parameter type (resulting in an ambiguous deduction), or

  • no function matches the function parameter type, or

  • the overload set supplied as an argument contains one or more function templates.



This may not be so obvious, so I'll provide an example:

这可能不是那么明显,所以我将提供一个例子:


void take(int);
void take(float);

template <typename T>
void foo(T* function_pointer) { function_pointer(0); }
// note: T(*function_pointer)(int) would work, and T would deduce to void

int main() { foo(&take); } // error

Both take(int) and take(float) match the function parameter, so it's ambiguous whether T should deduce to void(int) or void(float).

Take(Int)和Take(Float)都与函数参数匹配,因此T是否应该推导为VOID(INT)或VALID(FLOAT)是不明确的。


6. std::initializer_list function arguments



A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).



The example in the C++ standard demonstrates this well:

C++标准中的示例很好地说明了这一点:



template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T


Of all the bullets, this restriction is the most artificial. {1, 2, 3} could by considered std::initializer_list<int>, but it was intentionally decided not to make this deduction.

在所有的子弹中,这个限制是最人为的。{1,2,3}可以被认为是std::Initializer_list ,但它是故意决定不进行此推断的。


7. void foo(Ts..., int)



A function parameter pack that does not occur at the end of the parameter-declaration-list.



The template parameter pack Ts... cannot be deduced.
The basic issue is that if we called foo(0) this is ambiguous between providing 0 as an argument to the pack, or to the int parameter.
In function templates, this ambiguity is resolved by interpreting the parameter pack as an empty pack.

模板参数包Ts.无法推断。基本的问题是,如果我们调用foo(0),这是在将0作为参数提供给pack还是int参数之间的不明确性。在函数模板中,这种歧义通过将参数包解释为空包来解决。


Further notes


There numerous rules that need to be followed for deduction to be possible.
Not deducing from a non-deduced context is just one of them.
[temp.deduct.type] p8 lists the forms that a type must have for deduction to be possible.

有许多规则需要遵循才能进行扣减。不从非演绎的上下文中进行演绎只是其中之一。[temp.deduct.type]p8列出了一种类型要实现扣减所必须具备的形式。


Another indirect rule related to arrays is this:

另一条与数组相关的间接规则是:


template <int N> foo(int(&)[N]); // N can be deduced from array size
template <int N> foo(int[N]); // N cannot be deduced because arrays in parameters
// are adjusted to pointers

Intentionally disabling type deduction


Sometimes developers intentionally disable deduction because they want the user of a function template to provide an argument explicitly.
This can be done with std::type_identity in C++20, or with a user-defined version of it prior to C++20.

有时,开发人员故意禁用演绎,因为他们希望函数模板的用户显式提供参数。这可以使用C++20中的std::type_IDENTITY或C++20之前的用户定义版本来完成。


template <typename T>
void foo(std::type_identity<T>::type); // non-deduced context in function parameter

更多回答

So you are referencing the second case described by the standard ("A type that is a template-id in which one or more of the template-arguments is an expression that references a template-parameter."), right? If so, can you make an example of the first one ("The nested-name-specifier of a type that was specified using a qualified-id.")?

因此,您引用的是标准所描述的第二种情况(“一个类型是一个模板id,其中一个或多个模板参数是引用模板参数的表达式。”),对吗?如果是这样的话,您能举一个第一个例子(“使用限定id指定的类型的嵌套名称说明符”)吗?

Actually, now that I read it carefully, I think it's the opposite.

事实上,现在我仔细阅读了它,我认为它是相反的。

@Jefffrey: Maybe something like template <std::size_t> struct Bar; template <typename T> void(Bar<sizeof(T)>);?

@Jefffrey:也许类似于template struct Bar; template void(Bar<sizeof(T)>);?

The point is this: There is a one-to-one correspondence between types T and template classes Foo<T>, so you can deduce the former from the latter. But there is no correspondence between types T and arbitrary members Foo<T>::X.

问题是:类型T和模板类foo之间存在一一对应关系,因此可以从模板类foo推断出前者。但是类型T和任意成员foo::X之间没有对应关系。

@camino: no, I wouldn't say that -- it's not something technical or mechanical. There's a much more fundamental problem in principle that the mapping from name to type is not invertible, and so it doesn't make sense to take a type and ask for "the" name that defines it, because this very notion does not exist. Deduction only works for the much narrower question of "which type can be substituted into this particular given pattern to form a valid call", which is much more constrained, and hence meaningful.

@Camino:不,我不会这么说--这不是什么技术或机械的东西。原则上有一个更基本的问题,即从名称到类型的映射是不可逆的,因此获取一个类型并要求定义它的“名称”是没有意义的,因为这个概念并不存在。演绎只适用于一个更狭隘的问题,即“哪种类型可以被替换到这个特定的给定模式中以形成有效的调用”,这个问题要受更多的约束,因此也有意义。

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