为什么std :: result_of将(不相关的)函数类型作为类型参数?

时间:2021-12-23 18:51:32

I realize "why are things the way they are" questions are not usually the best, but there are many people on SO that are tuned to standard committee discussions so I hope this can be answered factually, as I'm legitimately curious as to what the answer is.

我意识到“为什么事情就像他们一样”问题通常不是最好的,但是很多人都在调整标准委员会讨论,所以我希望这可以在事实上得到回答,因为我对于什么是合法的好奇答案是。

Basically, it took me a long time to figure out what was going on with std::result_of's template signature the first time I saw it: I thought it was an entirely new construct for template parameters that I had never seen before.

基本上,我第一次看到它时,花了很长时间才弄清楚std :: result_of的模板签名是怎么回事:我认为这是一个全新的模板参数构造,这是我以前从未见过的。

template< class F, class... ArgTypes >
class result_of<F(ArgTypes...)>;

After some time thinking about it, I realized what this actually was: F(ArgTypes...) is a function type, but it's not the type of the function whose result type is being evaluated (that's just F): it's the type of a function taking ArgTypes... arguments and returning type F.

经过一段时间的思考,我意识到这实际上是什么:F(ArgTypes ...)是一个函数类型,但它不是正在评估结果类型的函数的类型(只是F):它是类型一个函数接受ArgTypes ...参数并返回F类型。

Isn't this...odd? Kind of hackish? Does anyone know if the committee ever discussed any alternatives, like, say, the following...

这不是......奇怪吗?有点hackish?有没有人知道委员会是否讨论过任何替代方案,例如,以下......

template< class F, class... ArgTypes >
class result_of<F, ArgTypes...>;

?

I guess it's possible that there's situations where the second construct can't be used as easily as the first one, but which ones?

我想有可能的情况是第二个结构不能像第一个结构那样容易使用,但是哪个?

I'm not trying to pass judgement on this, but it's just that this was legitimately confusing to me the first time I saw it, so I'm curious if there's a good reason for it. I realize part of the answer might simply be "because Boost did it" that way, but still that leave the remaining (factual) questions...

我并没有试图对此作出判断,但只是在我第一次看到它时,这对我来说是合法的混淆,所以我很好奇是否有充分的理由。我意识到部分答案可能只是“因为Boost这样做了”,但仍留下剩下的(事实)问题......

  • Is there a technical reason Boost choose this syntax to encode type information rather than any alternative?

    是否存在技术原因Boost选择此语法来编码类型信息而不是任何替代方案?

  • Was there any discussion by the C++11 committee about how appropriate it was to standardize this, given that std::result_of can be implemented in terms of decltype fairly easily anyway?

    考虑到std :: result_of可以相当容易地以decltype的形式实现,那么C ++ 11委员会是否有任何关于将其标准化是否合适的讨论?

4 个解决方案

#1


17  

Having a function-type as the parameter allows you to have an unrestricted "variadic" class template even in C++03. Think about it: In C++03, we didn't have variadic templates. And you can't "overload" a class template like you can with function templates - so how would it be otherwise possible to allow different amounts of "arguments" to the function?

使用函数类型作为参数,即使在C ++ 03中也可以使用不受限制的“可变参数”类模板。想一想:在C ++ 03中,我们没有可变参数模板。并且你不能像使用函数模板一样“重载”类模板 - 那么如何才能允许函数使用不同数量的“参数”呢?

Using a function type, you can just add any number partial specializations for the different number of parameters:

使用函数类型,您只需为不同数量的参数添加任意数量的部分特化:

template<class Fty>
struct result_of;

template<class F>
struct result_of<F()>{ /*...*/ };

template<class F, class A0>
struct result_of<F(A0)>{ /*...*/ };

template<class F, class A0, class A1>
struct result_of<F(A0, A1)>{ /*...*/ };

// ...

The only other way to do this in C++03 is default template arguments and partially specializing for every case - the disadvantage being that it doesn't look like a function call anymore, and that any kind of wrapper that uses result_of internally can't just pass Sig along.

在C ++ 03中执行此操作的唯一其他方法是默认模板参数,并且对每种情况都部分特殊 - 缺点是它看起来不再像函数调用,并且任何类型的内部使用result_of的包装器都可以'只是沿着Sig传递。


Now, there's one disadvantage with the function-type way - you also get all the usual transformations done to the "parameters": R(Args...) -> R(*)(Args...) and more importantly T[N] -> T* and top-level cv-qualifiers being discarded (§8.3.5/5):

现在,函数类型的方法有一个缺点 - 你也可以完成对“参数”的所有常规转换:R(Args ...) - > R(*)(Args ...),更重要的是T [ N] - > T *和*cv限定符被丢弃(§8.3.5/ 5):

struct X{
  bool operator()(int (&&arr)[3]);
  long operator()(void*);
};

static_assert(std::is_same<std::result_of<X(int[3])>::type, bool>(), "/cry");

Live example. Output:

实例。输出:

error: static assertion failed: /cry

错误:静态断言失败:/ cry

The other problems is with the top-level cv-qualifiers being discarded:

其他问题是*cv限定符被丢弃:

struct Y{};

struct X{
  bool operator()(Y const&);
  long operator()(Y&&);
};

Y const f();

static_assert(std::is_same<std::result_of<X(Y const)>::type, bool>(), "/cry");

Live example. Output:

实例。输出:

error: static assertion failed: /cry

错误:静态断言失败:/ cry

#2


9  

I think it's just that someone got the idea that you could (ab)use the function type notation to mimic the way the respective functor call would look like, and it stuck. So, no technical reasons, just an aesthetic one.

我认为只是有人认为你可以(ab)使用函数类型表示法来模仿各个函子调用的样子,并且它会卡住。所以,没有技术原因,只是美学原因。

// the result type of a call to (an object of) type F,
// passing (objects of) types A, B, and C as parameters.
result_of<F(A, B, C)>::type

#3


4  

result_of was part of TR1, which came out before decltype was added to the language. But it was designed with decltype in mind, so changing the implementation of result_of to use decltype is simple. Yes, it's a hack, but it works.

result_of是TR1的一部分,它在将decltype添加到语言之前出现。但它的设计考虑了decltype,因此将result_of的实现更改为使用decltype很简单。是的,这是一个黑客,但它的工作原理。

#4


2  

(This expands on JohannesD's answer and Jesse Good's comment on it, but this won't fit in a comment. Please upvote that other answer not this one.)

(这扩展了JohannesD的回答和Jesse Good对此的评论,但这不符合评论。请注意其他答案不是这个。)

From N1454 Syntax and examples:

来自N1454语法和示例:

The definition of the behavior of result_of is straightforward: given types F, T1, T2, ..., TN and lvalues f, t1, t2, ..., tN of those types, respectively, the type expression

result_of行为的定义很简单:分别给出类型F,T1,T2,...,TN和左值f,t1,t2,...,tN,类型表达式

result_of<F(T1, T2, ..., TN)>::type

evaluates to the type of the expression f(t1, t2, ..., tN).

计算表达式f(t1,t2,...,tN)的类型。

This is not abusing the type system, it's beautifully elegant!

这不是滥用类型系统,它非常优雅!

#1


17  

Having a function-type as the parameter allows you to have an unrestricted "variadic" class template even in C++03. Think about it: In C++03, we didn't have variadic templates. And you can't "overload" a class template like you can with function templates - so how would it be otherwise possible to allow different amounts of "arguments" to the function?

使用函数类型作为参数,即使在C ++ 03中也可以使用不受限制的“可变参数”类模板。想一想:在C ++ 03中,我们没有可变参数模板。并且你不能像使用函数模板一样“重载”类模板 - 那么如何才能允许函数使用不同数量的“参数”呢?

Using a function type, you can just add any number partial specializations for the different number of parameters:

使用函数类型,您只需为不同数量的参数添加任意数量的部分特化:

template<class Fty>
struct result_of;

template<class F>
struct result_of<F()>{ /*...*/ };

template<class F, class A0>
struct result_of<F(A0)>{ /*...*/ };

template<class F, class A0, class A1>
struct result_of<F(A0, A1)>{ /*...*/ };

// ...

The only other way to do this in C++03 is default template arguments and partially specializing for every case - the disadvantage being that it doesn't look like a function call anymore, and that any kind of wrapper that uses result_of internally can't just pass Sig along.

在C ++ 03中执行此操作的唯一其他方法是默认模板参数,并且对每种情况都部分特殊 - 缺点是它看起来不再像函数调用,并且任何类型的内部使用result_of的包装器都可以'只是沿着Sig传递。


Now, there's one disadvantage with the function-type way - you also get all the usual transformations done to the "parameters": R(Args...) -> R(*)(Args...) and more importantly T[N] -> T* and top-level cv-qualifiers being discarded (§8.3.5/5):

现在,函数类型的方法有一个缺点 - 你也可以完成对“参数”的所有常规转换:R(Args ...) - > R(*)(Args ...),更重要的是T [ N] - > T *和*cv限定符被丢弃(§8.3.5/ 5):

struct X{
  bool operator()(int (&&arr)[3]);
  long operator()(void*);
};

static_assert(std::is_same<std::result_of<X(int[3])>::type, bool>(), "/cry");

Live example. Output:

实例。输出:

error: static assertion failed: /cry

错误:静态断言失败:/ cry

The other problems is with the top-level cv-qualifiers being discarded:

其他问题是*cv限定符被丢弃:

struct Y{};

struct X{
  bool operator()(Y const&);
  long operator()(Y&&);
};

Y const f();

static_assert(std::is_same<std::result_of<X(Y const)>::type, bool>(), "/cry");

Live example. Output:

实例。输出:

error: static assertion failed: /cry

错误:静态断言失败:/ cry

#2


9  

I think it's just that someone got the idea that you could (ab)use the function type notation to mimic the way the respective functor call would look like, and it stuck. So, no technical reasons, just an aesthetic one.

我认为只是有人认为你可以(ab)使用函数类型表示法来模仿各个函子调用的样子,并且它会卡住。所以,没有技术原因,只是美学原因。

// the result type of a call to (an object of) type F,
// passing (objects of) types A, B, and C as parameters.
result_of<F(A, B, C)>::type

#3


4  

result_of was part of TR1, which came out before decltype was added to the language. But it was designed with decltype in mind, so changing the implementation of result_of to use decltype is simple. Yes, it's a hack, but it works.

result_of是TR1的一部分,它在将decltype添加到语言之前出现。但它的设计考虑了decltype,因此将result_of的实现更改为使用decltype很简单。是的,这是一个黑客,但它的工作原理。

#4


2  

(This expands on JohannesD's answer and Jesse Good's comment on it, but this won't fit in a comment. Please upvote that other answer not this one.)

(这扩展了JohannesD的回答和Jesse Good对此的评论,但这不符合评论。请注意其他答案不是这个。)

From N1454 Syntax and examples:

来自N1454语法和示例:

The definition of the behavior of result_of is straightforward: given types F, T1, T2, ..., TN and lvalues f, t1, t2, ..., tN of those types, respectively, the type expression

result_of行为的定义很简单:分别给出类型F,T1,T2,...,TN和左值f,t1,t2,...,tN,类型表达式

result_of<F(T1, T2, ..., TN)>::type

evaluates to the type of the expression f(t1, t2, ..., tN).

计算表达式f(t1,t2,...,tN)的类型。

This is not abusing the type system, it's beautifully elegant!

这不是滥用类型系统,它非常优雅!