简体   繁体   English

std :: function的copy-constructor是否要求模板类型的参数类型为完整类型?

[英]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 be CopyConstructible . 要求: F必须是CopyConstructible f shall be Callable (20.8.11.2) for argument types ArgTypes and return type R . 对于参数类型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 type F is Callable for argument types ArgTypes and return type R if the expression INVOKE (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) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T ; (t1.*f)(t2, ..., tN)f是指向类T的成员函数的指针并且t1是类型T的对象或对类型T的对象的引用或对T的引用从T派生的类型的对象;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 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) as INVOKE (f, t1, t2, ..., tN) implicitly converted to R . 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) is R(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 type F is Callable for argument types ArgTypes and return type R 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: 我认为最重要的结论是以下要求:

  • given a callable object that is Callable with signature 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表达式, RA...是完整的(或者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未描述为可调用对象或用于任何签名
  • invoking an 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])
  • constructing an 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段)
  • calling the 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::functiontarget成员是带有std::function的调用签名的Callable (20.8.11.2.5函数目标访问[func.wrap.func.targ])
  • all other operations of 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(*), CallableINVOKE进行描述,否则要求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.

相关问题 获取std :: function可变参数模板参数类型的自定义sizeof - Get custom sizeof of std::function's variadic template argument types 如何强制模板要求 function 谓词可使用类型或任何该类型的派生类型调用? - How to enforce a template to require a function predicate callable with a type or any of that type's derived types? std :: function参数类型 - std::function argument types std :: list模板的实例类型是否需要副本构造函数(或等效副本)吗? - Does std::list template require a copy constructor (or equivalent) in its instance type? ADL with std :: function:可以通过std :: function的参数列表中的类型找到带std :: function对象的函数吗? - ADL with std::function: Can functions taking std::function objects be found via the types in the std::function's argument list? 为什么复制构造函数参数为const? - Why is the copy-constructor argument const? 具有类型转换的多态复制构造函数 - Polymorphic copy-constructor with type conversion 是否可以从(仿函数成员的)function 签名中检索参数类型以在模板中使用? - Is it possible to retrieve the argument types from a (Functor member's) function signature for use in a template? C ++ std :: function返回其自身类型的向量(再次是递归类型) - C++ std::function that returns a vector of it's own type (recursive types again) 在哪些情况下需要特别指定模板的参数“types”? - In which cases one needs to specify the template's argument `types` specifically?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM