[英]Does std::function's copy-constructor require the template type's argument types to be complete types?
Given: 鉴于:
#include <functional>
class world_building_gun;
class tile_bounding_box;
typedef std::function<void (world_building_gun, tile_bounding_box)> worldgen_function_t;
void foo() {
worldgen_function_t v;
worldgen_function_t w(v);
}
Should this compile? 应该编译吗? My compilers say:
我的编译器说:
Yes: GCC/stdlibc++ (also boost::function is yes in both GCC and Clang) 是:GCC / stdlibc ++(在GCC和Clang中,boost :: function是yes)
No: Clang/libc++ ( http://libcxx.llvm.org/ , Clang 3.0, libc++ SVN as of today) 否:Clang / libc ++( 到目前为止 , http ://libcxx.llvm.org/,Clang 3.0,libc ++ SVN)
(If "no" is the correct answer, I will fix my real code to put complete types in more headers or use boost::function.) (如果“ no”是正确的答案,我将修复我的真实代码,以将完整的类型放入更多的标头中,或使用boost :: function。)
EDIT: Here is the Clang error message: 编辑:这是Clang错误消息:
In file included from foo.cpp:2:
In file included from /usr/include/c++/v1/functional:462:
/usr/include/c++/v1/type_traits:2766:19: error: invalid appli
static_assert(sizeof(_Tp) > 0, "Type must be complete.");
^~~~~~~~~~~
/usr/include/c++/v1/type_traits:2752:15: note: in instantiation of template class 'std::__1::__check_complete<world_buildin
: private __check_complete<_Hp>,
^
/usr/include/c++/v1/type_traits:2753:15: note: in instantiation of template class 'std::__1::__check_complete<world_buildin
private __check_complete<_T0, _Tp...>
^
/usr/include/c++/v1/type_traits:2919:15: note: in instantiation of template class 'std::__1::__check_complete<std::__1::fun
world_building_gun, tile_bounding_box>' requested here
: private __check_complete<_Fp, _Args...>
^
/usr/include/c++/v1/type_traits:2930:11: note: in instantiation of template class 'std::__1::__invokable_imp<std::__1::func
world_building_gun, tile_bounding_box>' requested here
__invokable_imp<_Fp, _Args...>::value>
^
/usr/include/c++/v1/functional:1115:33: note: in instantiation of template class 'std::__1::__invokable<std::__1::function<
world_building_gun, tile_bounding_box>' requested here
template <class _Fp, bool = __invokable<_Fp&, _ArgTypes...>::value>
^
/usr/include/c++/v1/functional:1141:35: note: in instantiation of default argument for '__callable<std::__1::function<void (world_building_gun, tile_bounding_box)> >' required here
typename enable_if<__callable<_Fp>::value>::type* = 0);
^~~~~~~~~~~~~~~
/usr/include/c++/v1/functional:1140:7: note: while substituting deduced template arguments into function template 'function' [with _Fp = std::__1::function<void
(world_building_gun, tile_bounding_box)>]
function(_Fp,
^
foo.cpp:4:7: note: forward declaration of 'world_building_gun'
class world_building_gun;
^
In file included from foo.cpp:2:
In file included from /usr/include/c++/v1/functional:462:
/usr/include/c++/v1/type_traits:2766:19: error: invalid application of 'sizeof' to an incomplete type 'tile_bounding_box'
static_assert(sizeof(_Tp) > 0, "Type must be complete.");
^~~~~~~~~~~
/usr/include/c++/v1/type_traits:2753:15: note: in instantiation of template class 'std::__1::__check_complete<tile_bounding_box>' requested here
private __check_complete<_T0, _Tp...>
^
/usr/include/c++/v1/type_traits:2753:15: note: in instantiation of template class 'std::__1::__check_complete<world_building_gun, tile_bounding_box>' requested here
private __check_complete<_T0, _Tp...>
^
/usr/include/c++/v1/type_traits:2919:15: note: in instantiation of template class 'std::__1::__check_complete<std::__1::function<void (world_building_gun, tile_bounding_box)> &,
world_building_gun, tile_bounding_box>' requested here
: private __check_complete<_Fp, _Args...>
^
/usr/include/c++/v1/type_traits:2930:11: note: in instantiation of template class 'std::__1::__invokable_imp<std::__1::function<void (world_building_gun, tile_bounding_box)> &,
world_building_gun, tile_bounding_box>' requested here
__invokable_imp<_Fp, _Args...>::value>
^
/usr/include/c++/v1/functional:1115:33: note: in instantiation of template class 'std::__1::__invokable<std::__1::function<void (world_building_gun, tile_bounding_box)> &,
world_building_gun, tile_bounding_box>' requested here
template <class _Fp, bool = __invokable<_Fp&, _ArgTypes...>::value>
^
/usr/include/c++/v1/functional:1141:35: note: in instantiation of default argument for '__callable<std::__1::function<void (world_building_gun, tile_bounding_box)> >' required here
typename enable_if<__callable<_Fp>::value>::type* = 0);
^~~~~~~~~~~~~~~
/usr/include/c++/v1/functional:1140:7: note: while substituting deduced template arguments into function template 'function' [with _Fp = std::__1::function<void
(world_building_gun, tile_bounding_box)>]
function(_Fp,
^
foo.cpp:5:7: note: forward declaration of 'tile_bounding_box'
class tile_bounding_box;
^
2 errors generated.
Clang+libc++ compiles successfully if I delete the line "worldgen_function_t w(v);" 如果删除“ worldgen_function_t w(v);”行,则Clang + libc ++编译成功。 or if I make the classes complete types.
或者如果我将类设置为完整类型。
Edit: Apperently , this issue is now fixed, so the below text can be seen as history. 编辑: 显然 ,此问题已修复,因此以下文本可以视为历史记录。 :)
:)
The issue is indeed (as I predicted) with libc++'s SFINAE checks in the templated ctor (for a reasoning, check this question ). 这个问题确实是(正如我所预测的)libc ++的SFINAE在模板化的ctor中进行检查(出于某种原因,请检查此问题 )。 It checks if the following (for example) is valid and gives a nice and clean error at the construction site rather than deep inside the guts of
std::function
(try the following example with libstd++ or MSVC... shudder ): 它会检查以下(例如)是有效的,并给出在施工现场一个非常干净的错误,而不是深部的内脏里
std::function
(尝试用下面的例子libstd ++或MSVC ... 不寒而栗 ):
#include <functional>
void f(int* p){}
int main(){
std::function<void(int)> fun(f);
}
libc++ will cause the compiler to spit out something along the lines of "no constructor found that matches the argument list void (*)(int*)
", since the only applicable one (the templated ctor) gets SFINAE'd out. libc ++将导致编译器按照“找不到与参数列表
void (*)(int*)
匹配的构造函数”的方式吐出某些东西,因为唯一适用的变量(模板化ctor)被SFINAE淘汰了。
However, so that the __callable
and __invoke_imp
checks work, the argument and return types need to be complete, since otherwise implicit conversions wouldn't be taken into account here. 但是,为了使
__callable
和__invoke_imp
检查工作正常,参数和返回类型必须完整,因为在此否则将不考虑隐式转换。
The reason that the templated ctor is even looked at is that all ctors are enumerated before considering a best match (in this case the copy ctor). 甚至查看模板化ctor的原因是,在考虑最佳匹配之前(在这种情况下为复制ctor),已枚举了所有ctor。
Now, the standard is very clear that the argument and return types need to be complete when constructing a std::function
object from a callable object (aka calling the templated ctor): 现在,该标准非常清楚,从可调用对象(也称为调用模板化ctor)构造
std::function
对象时,参数和返回类型必须完整:
§20.8.11.2.1 [func.wrap.func.con] p7
template <class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
Requires:
F
shall beCopyConstructible
.要求:
F
必须是CopyConstructible
。f
shall be Callable (20.8.11.2) for argument typesArgTypes
and return typeR
.对于参数类型
ArgTypes
和返回类型R
f
应为Callable (20.8.11.2)。 [...][...]
(Note: "Requires" addresses the user of the functionality, not the implementer.) (注意:“要求”针对的是功能的用户 ,而不是实现者。)
§20.8.11.2 [func.wrap.func] p2
A callable object
f
of typeF
is Callable for argument typesArgTypes
and return typeR
if the expressionINVOKE
(f, declval<ArgTypes>()..., R)
, considered as an unevaluated operand (Clause 5), is well formed (20.8.2).一个可调用对象
f
的类型F
为参数类型是可赎回ArgTypes
和返回类型R
如果表达式INVOKE
(f, declval<ArgTypes>()..., R)
视为未计算的操作数(第5章),是公形成 (20.8.2)。
§20.8.2 [func.req]
p1 Define
INVOKE
(f, t1, t2, ..., tN)
as follows:p1如下定义
INVOKE
(f, t1, t2, ..., tN)
:
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;(t1.*f)(t2, ..., tN)
当f
是指向类T
的成员函数的指针并且t1
是类型T
的对象或对类型T
的对象的引用或对T
的引用从T
派生的类型的对象;((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;((*t1).*f)(t2, ..., tN)
当f
是指向类T
的成员函数的指针并且t1
不是上一项中描述的类型之一时;- [...]
[...]
f(t1, t2, ..., tN)
in all other cases.在所有其他情况下
f(t1, t2, ..., tN)
p2 Define
INVOKE
(f, t1, t2, ..., tN, R)
asINVOKE
(f, t1, t2, ..., tN)
implicitly converted toR
.p2将
INVOKE
(f, t1, t2, ..., tN, R)
INVOKE
(f, t1, t2, ..., tN, R)
为INVOKE
(f, t1, t2, ..., tN)
隐式转换为R
So, libc++ is certainly within its rights to do the SFINAE check in the templated ctor, since the types need to be complete, since otherwise you'd get undefined behaviour. 因此,libc ++当然有权在模板化的ctor中执行SFINAE检查,因为类型必须完整,否则您将获得不确定的行为。 However, it may be a bit unfortunate and be considered a defect that the safety check for a complete type triggers even if the actual SFINAE check is never needed (because the copy ctor will always be invoked).
但是,可能会有点不幸,即使完全不需要实际的SFINAE检查(因为将始终调用复制ctor),也会触发针对完整类型的安全检查触发的缺陷。 This may be alleviated by making the
callable
check a lazy one, like 可以通过使
callable
检查成为懒惰的检查来缓解这种情况,例如
template<bool Copy, class F>
struct lazy_callable{
static bool const value = callable<F>::value;
};
template<class F>
struct lazy_callable<true, F>{
static bool const value = false;
};
template<class F>
function(F f, typename enable_if<lazy_callable<!std::is_same<F,function>::value>::type* = 0);
This should only trigger the callable
SFINAE check if F
is not actually std::function<...>
. 如果
F
实际上不是std::function<...>
则这仅应触发callable
SFINAE检查。
Man, I may have digressed a bit here at the end... 伙计,我可能最后在这里离题了...
I've committed a fix to libc++ such that this example now compiles, committed revision 160285. 我已经提交了对libc ++的修复,以便此示例现在可以进行编译,提交的修订版为160285。
I believe libc++ was being overly aggressive in checking the argument list for complete types. 我相信libc ++在检查参数列表中的完整类型时过于激进。
I'd say no. 我会说不。 From 20.8.11.2 Class template function [func.wrap.func], we have:
从20.8.11.2类模板函数[func.wrap.func]开始,我们有:
3 The
function
class template is a call wrapper (20.8.1) whose call signature (20.8.1) isR(ArgTypes...)
.3
function
类模板是一个调用包装(20.8.1),其调用签名(20.8.1)为R(ArgTypes...)
。
In 20.8.1 Definitions [func.def ], we get the following definitions as to what constitutes a call wrapper type, a call wrapper and a call signature: 在20.8.1定义[func.def]中,我们获得了有关构成调用包装类型,调用包装和调用签名的以下定义:
2 A call signature is the name of a return type followed by a parenthesized comma-separated list of zero or more argument types.
2呼叫签名是返回类型的名称,后跟用括号括住的逗号分隔的零个或多个参数类型列表。
5 A call wrapper type is a type that holds a callable object and supports a call operation that forwards to that object.
5调用包装器类型是一种包含可调用对象并支持转发到该对象的调用操作的类型。
6 A call wrapper is an object of a call wrapper type.
6呼叫包装器是呼叫包装器类型的对象。
Notice how paragraph 2 doesn't mention completeness of types. 请注意,第2段没有提到类型的完整性。
To cut a story short (a lot of definitions are involved), the meaning of 'callable object' here means either a functor (the familiar notion, ie something that can be used like a function) or a pointer-to-member. 为了简化一个故事(涉及很多定义),这里的“可调用对象”的意思是函子(熟悉的概念,即可以像函数一样使用的东西)或指向成员的指针。 Furthermore the Standard also describe the Callable concept in 20.8.11.2 paragraph 2:
此外,该标准还在20.8.11.2第2段中描述了Callable概念:
A callable object
f
of typeF
is Callable for argument typesArgTypes
and return typeR
if the expression INVOKE(f, declval<ArgTypes>()..., R)
, considered as an unevaluated operand (Clause 5), is well formed (20.8.2).如果表达式INVOKE
(f, declval<ArgTypes>()..., R)
F
类型的可调用对象f
调用为参数类型ArgTypes
并返回类型R
(20.8.2)。
(The INVOKE bit is an imaginary function that the Standard uses to define how functors and pointers to members are, well, invoked.) ( INVOKE位是一个虚构的函数,标准用来定义如何调用成员的函子和指针。)
What I think is the most important conclusion from that is the following requirement: 我认为最重要的结论是以下要求:
R(A...)
, then R
and A...
are complete (or possibly R
is void
) by virtue of the INVOKE expression (ie otherwise it would not be well-formed, notice the use of declval<ArgTypes>()...
) R(A...)
Callable对象,则借助于INVOKE表达式, R
和A...
是完整的(或者R
可能是void
)(即否则格式不正确,注意使用declval<ArgTypes>()...
) My argument now rests on '[the] call operation that forwards to that object' in the definition of call wrappers, which I think is intentionally left vague so as to not be overly restrictive. 我的论点现在基于调用包装的定义中的“转发到该对象的调用操作”,我认为这是故意含糊的,以免过于严格。 In the case of
std::function<Sig>
where some incomplete types are involved in Sig
then we could define this operation as being 'first complete the types, then treat std::function<Sig>
as a callable object type of call signature Sig
'. 在的情况下
std::function<Sig>
其中一些不完全类型参与Sig
然后我们可以定义这个操作为“先完成类型, 那么治疗std::function<Sig>
呼叫签名的一个可调用对象类型Sig
'。
Given that, here are the key points of my argument: 鉴于此,以下是我的论点:
std::function
is not described as being a callable object or as being Callable for any signature std::function
未描述为可调用对象或可用于任何签名 std::function
is done in terms of INVOKE (20.8.11.2.4 function invocation [func.wrap.func.inv]) std::function
是根据INVOKE进行的 (20.8.11.2.4函数调用[func.wrap.func.inv]) std::function
from a callable object is in terms of Callable with the call signature of std::function
(20.8.11.2.1 function construct/copy/destroy [func.wrap.func.con] paragraph 7) std::function
方式是带有std::function
的调用签名的Callable (20.8.11.2.1函数construct / copy / destroy [func.wrap.func.con]第7段) target
member of std::function
is in terms of Callable with the call signature of std::function
(20.8.11.2.5 function target access [func.wrap.func.targ]) std::function
的target
成员是带有std::function
的调用签名的Callable (20.8.11.2.5函数目标访问[func.wrap.func.targ]) std::function
are not described in terms of callable object(*) , Callable , INVOKE or otherwise require that the call signature of std::function
involve complete types std::function
所有其他操作未按照callable object(*), Callable , INVOKE进行描述,否则要求std::function
的调用签名涉及完整类型 (*) except in the case of one constructor where the description contains "shall not throw exceptions if f
's target is a callable object passed via reference_wrapper
or a function pointer". (*),除非在一个构造函数中描述包含“如果
f
的目标是通过reference_wrapper
或函数指针传递的可调用对象,则不应引发异常”。 I think in the context it's clear that this doesn't affect the argument. 在上下文中,我认为这显然不会影响论点。 For what it's worth this constructor is not involved in the snippet of the OP.
值得的是,此构造函数不包含在OP的代码段中。
So I'd say unless you do use one of those operations that indirectly require the signature to involve complete types, you're good to go. 因此,我要说的是,除非您确实使用间接需要签名包含完整类型的那些操作之一,否则您还是可以的。
It's all well and good to analyze what the Standard prescribes but it's also important to consider what is the intent of the Standard. 分析该标准的规定是一件好事,但考虑该标准的意图也很重要。 In this case I think it is very much desirable and expected that
std::function
does not require that the types of the call signature be complete. 在这种情况下,我认为非常期望并且期望
std::function
不需要呼叫签名的类型完整。 Consider the following: 考虑以下:
// in a_fwd.hpp
struct incomplete;
using callback_type = std::function<void(incomplete)>;
callback_type make_callback();
// in b.hpp; depends on a_fwd.hpp
#include "a_fwd.hpp"
void eat_callback(callback_type);
Then without the requirement an unrelated TU, let's call it C, that is a client of B can do: 然后,不需要一个不相关的TU,我们将其称为C,即B的客户可以做到:
// in c.cpp
#include "b.hpp"
// somewhere in e.g. a function body
eat_callback(make_callback());
This is type-safe and minimizes the coupling since only translation unit B need to know about the details of translation unit A. 这是类型安全的,并且由于只有翻译单元B才需要了解翻译单元A的详细信息,因此可以最大程度地减少耦合。
Furthermore both Boost.Function and libstdc++ have demonstrated that it is possible to implement std::function
without such a requirement. 此外,Boost.Function和libstdc ++都证明可以实现
std::function
而无需这样的要求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.