为什么类型约束' std::convertible_to '只能用于一个模板参数?



我已经在标准和cppreference中滚动和搜索了几个小时,但无济于事,如果有人能给我解释一下这个现象,我将非常感激:

我正在看标准概念std::convertibe_to。这里有一个我理解的简单例子

class A {};
class B : public A {};
std::convertible_to<A, B>; // false
std::convertible_to<B, A>; // true

工作正常

现在还有另一种可能的用法,我不太明白

void foo(std::convertible_to<A> auto x) { /* ... */ }

,这个函数可以很容易地接受任何可转换为a的类型,这很奇怪,因为第一个模板形参("From")基本上被删除了,并在函数调用时推导出来。下面的函数也可以工作,而且我相当确定它实际上相当于前面的

template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
当我们调用foo时,再一次推导出x的类型。

尽管模板需要两个参数,但这仍然有效。我也尝试了std::derived_from,它似乎有效。这种只用一个模板参数指定概念的形式甚至出现在标准本身中,所以必须有一些语法来解释它。

注意,唯一存在的std::convertible_to版本实际上是接受两个模板形参的版本。

谁能解释为什么这是有效的?

void foo( constraint<P0, P1, P2> auto x );

大致翻译为

template<contraint<P0, P1, P2> X>
void foo( X x );

大致翻译为

template<class X> requires constraint<X, P0, P1, P2>
void foo( X x );

注意类型X是如何在约束的模板参数前加上的。

在你的例子中,

template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }

template<typename T, class S>
requires std::convertible_to<S, T>
void foo(S x) { /* ... */ }

(我说大致,因为我相信它们在微妙的方面并不完全等同。例如,第二个引入了名称X,而第一个没有。可能还有其他类似规模的差异;我的意思是,理解翻译会让你理解翻译的内容。这与for(:)循环-for(;;)循环对应不同;标准根据for(;;)循环指定for(:)循环,这不是我上面所说的。

在模板参数列表中没有提供模板概念的第一个参数的情况下,可以使用concept名称。约束auto推导出的变量就是其中之一。

在这些情况下,第一个参数由表达式提供,通常使用模板参数推导规则。在约束函数形参的情况下,第一个实参由模板函数本身决定。也就是说,如果调用foo(10),模板实参演绎将把auto模板形参演绎为int。因此,完整的概念将是convertible_to<int, A>

相关内容