<i id='E9IEa'><tr id='E9IEa'><dt id='E9IEa'><q id='E9IEa'><span id='E9IEa'><b id='E9IEa'><form id='E9IEa'><ins id='E9IEa'></ins><ul id='E9IEa'></ul><sub id='E9IEa'></sub></form><legend id='E9IEa'></legend><bdo id='E9IEa'><pre id='E9IEa'><center id='E9IEa'></center></pre></bdo></b><th id='E9IEa'></th></span></q></dt></tr></i><div id='E9IEa'><tfoot id='E9IEa'></tfoot><dl id='E9IEa'><fieldset id='E9IEa'></fieldset></dl></div>

      <small id='E9IEa'></small><noframes id='E9IEa'>

      • <bdo id='E9IEa'></bdo><ul id='E9IEa'></ul>
    1. <tfoot id='E9IEa'></tfoot>
    2. <legend id='E9IEa'><style id='E9IEa'><dir id='E9IEa'><q id='E9IEa'></q></dir></style></legend>
    3. 使用 std::function 重载解析

      时间:2023-06-30
        <tbody id='3iuFc'></tbody>

        <tfoot id='3iuFc'></tfoot>

      1. <small id='3iuFc'></small><noframes id='3iuFc'>

        • <bdo id='3iuFc'></bdo><ul id='3iuFc'></ul>
            <legend id='3iuFc'><style id='3iuFc'><dir id='3iuFc'><q id='3iuFc'></q></dir></style></legend>
            • <i id='3iuFc'><tr id='3iuFc'><dt id='3iuFc'><q id='3iuFc'><span id='3iuFc'><b id='3iuFc'><form id='3iuFc'><ins id='3iuFc'></ins><ul id='3iuFc'></ul><sub id='3iuFc'></sub></form><legend id='3iuFc'></legend><bdo id='3iuFc'><pre id='3iuFc'><center id='3iuFc'></center></pre></bdo></b><th id='3iuFc'></th></span></q></dt></tr></i><div id='3iuFc'><tfoot id='3iuFc'></tfoot><dl id='3iuFc'><fieldset id='3iuFc'></fieldset></dl></div>

              1. 本文介绍了使用 std::function 重载解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

                问题描述

                考虑这个代码示例:

                #include #include <功能>typedef std::functionfunc1_t;typedef std::functionfunc2_t;结构体{X (func1_t f){ }X (func2_t f){ }};内部主(){X x([](){ std::cout << "Hello, world!
                "; });}

                我确信它不应该编译,因为编译器不应该能够选择两个构造函数之一.g++-4.7.3 显示了这种预期行为:它表示重载构造函数的调用不明确.但是g++-4.8.2编译成功.

                这段代码在 C++11 中是正确的还是这个版本的 g++ 的错误/特性?

                解决方案

                In C++11...

                我们来看看std::function的构造函数模板的规范(它接受任何Callable):[func.wrap.func.con]/7-10

                <块引用>

                template函数(F f);模板<F类,A类>函数(allocator_arg_t,const A& a,F f);

                7 要求: F 应该是 CopyConstructible.对于参数类型 ArgTypes 和返回类型,f 应为 Callable (20.10.11.2)R.A 的拷贝构造函数和析构函数不得抛出例外.

                8 后置条件: !*this 如果以下任何一项成立:

                • f 是一个 NULL 函数指针.
                • f 是指向成员的 NULL 指针.
                • F 是函数类模板的实例,!f

                9 否则,*thisstd::move(f) 初始化的f 副本为目标.[这里省略了一个注释]

                10 抛出:f 是函数指针或 reference_wrapper 时不应抛出异常,用于某些 T.否则,可能会抛出bad_allocF 的复制或移动构造函数抛出的任何异常.

                现在,从 [](){} 构建或尝试构建(用于重载解析)std::function(即带有签名void(void))违反了std::function的构造函数的要求.

                [res.on.required]/1

                <块引用>

                违反函数的 Requires: 段落中指定的先决条件会导致未定义的行为,除非函数的 Throws: 段落指定在违反先决条件时抛出异常.>

                所以,AFAIK,即使是重载决议的结果也是未定义的.因此,g++/libstdc++ 的两个版本都符合这一点.

                <小时>

                在 C++14 中,这已更改,请参阅 LWG 2132.现在,std::function 的转换构造函数模板需要 SFINAE-reject 不兼容的 Callables(下一章将详细介绍 SFINAE):

                <块引用>

                template函数(F f);模板<F类,A类>函数(allocator_arg_t,const A& a,F f);

                7 要求: F 应该是 CopyConstructible.

                8 备注: 这些构造函数不应参与重载解析,除非 f 对于参数类型是可调用的 (20.9.11.2)ArgTypes... 和返回类型 R.

                [...]

                不应参与重载决议"对应于通过 SFINAE 的拒绝.最终效果是,如果您有一组重载函数 foo,

                void foo(std::function);void foo(std::function);

                和一个调用表达式,例如

                foo([](std::string){})//(C)

                然后 foo 的第二个重载被明确选择:因为 std::functionF 定义为它的外部接口,F 定义了哪些参数类型被传递到 std::function.然后,必须使用这些参数(参数类型)调用包装的函数对象.如果将 double 传递给 std::function,则它不能传递给采用 std::string 的函数,因为没有转换 double -> std::string.对于 foo 的第一次重载,参数 [](std::string){} 因此不被视为可调用用于 std::function.构造函数模板已停用,因此没有从 [](std::string){}std::function 的可行转换.第一个重载从用于解析调用 (C) 的重载集中移除,只留下第二个重载.

                请注意,由于 LWG 2420,上述措辞略有变化:有一个例外,如果 std::function 的返回类型 Rvoid,则上述构造函数模板中的 Callable 的任何返回类型都将被接受(并丢弃).例如, []() ->void {}[]() ->bool {} 对于 std::function 是可调用的.因此,以下情况会产生歧义:

                void foo(std::function);void foo(std::function);foo([]() -> bool {});//模糊的

                重载解析规则不会尝试在不同的用户定义转换之间进行排名,因此 foo 的两个重载都是可行的(首先)并且两者都不是更好.

                <小时>

                SFINAE 在这方面有什么帮助?

                请注意,当 SFINAE 检查失败时,程序不是格式错误的,但该函数不适用于重载解析.例如:

                #include #include 模板自动 foo(T) ->类型名称 std::enable_if自动 foo(T) ->类型名称 std::enable_if<不是 std::is_integral<T>::value >::type{ std::cout <<"foo 2
                ";}int main(){富(42);富(42.);}

                同样,可以通过在转换构造函数上使用 SFINAE 使转换不可行:

                #include #include 结构体{模板<类T,类=类型名称 std::enable_if

                Consider this example of code:

                #include <iostream>
                #include <functional>
                
                typedef std::function<void()> func1_t;
                typedef std::function<void(int)> func2_t;
                
                struct X
                {
                   X (func1_t f)
                   { }
                
                   X (func2_t f)
                   { }
                };
                
                int main ( )
                {
                   X x([](){ std::cout << "Hello, world!
                "; });
                }
                

                I was sure that it shouldn't compile, because the compiler shouldn't be able to choose one of the two constructors. g++-4.7.3 shows this expected behavior: it says that call of overloaded constructor is ambiguous. However, g++-4.8.2 successfully compiles it.

                Is this code correct in C++11 or it is a bug/feature of this version of g++?

                解决方案

                In C++11...

                Let's take a look at the specification of the constructor template of std::function (which takes any Callable): [func.wrap.func.con]/7-10

                template<class F> function(F f);
                template <class F, class A> function(allocator_arg_t, const A& a, F f);
                

                7 Requires: F shall be CopyConstructible. f shall be Callable (20.10.11.2) for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

                8 Postconditions: !*this if any of the following hold:

                • f is a NULL function pointer.
                • f is a NULL pointer to member.
                • F is an instance of the function class template, and !f

                9 Otherwise, *this targets a copy of f initialized with std::move(f). [left out a note here]

                10 Throws: shall not throw exceptions when f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw bad_alloc or any exception thrown by F’s copy or move constructor.

                Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)> from a [](){} (i.e. with signature void(void)) violates the requirements of std::function<void(int)>'s constructor.

                [res.on.required]/1

                Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.

                So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.


                In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):

                template<class F> function(F f);
                template <class F, class A> function(allocator_arg_t, const A& a, F f);
                

                7 Requires: F shall be CopyConstructible.

                8 Remarks: These constructors shall not participate in overload resolution unless f is Callable (20.9.11.2) for argument types ArgTypes... and return type R.

                [...]

                The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo,

                void foo(std::function<void(double)>);
                void foo(std::function<void(char const*)>);
                

                and a call-expression such as

                foo([](std::string){}) // (C)
                

                then the second overload of foo is chosen unambiguously: Since std::function<F> defines F as its interface to the outside, the F defines which argument types are passed into std::function. Then, the wrapped function object has to be called with those arguments (argument types). If a double is passed into std::function, it cannot be passed on to a function taking a std::string, because there's no conversion double -> std::string. For the first overload of foo, the argument [](std::string){} is therefore not considered Callable for std::function<void(double)>. The constructor template is deactivated, hence there's no viable conversion from [](std::string){} to std::function<void(double)>. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.

                Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R of a std::function<R(ArgTypes...)> is void, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {} and []() -> bool {} are Callable for std::function<void()>. The following situation therefore produces an ambiguity:

                void foo(std::function<void()>);
                void foo(std::function<bool()>);
                
                foo([]() -> bool {}); // ambiguous
                

                The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo are viable (first of all) and neither is better.


                How can SFINAE help here?

                Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:

                #include <type_traits>
                #include <iostream>
                
                template<class T>
                auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
                {  std::cout << "foo 1
                ";  }
                
                template<class T>
                auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
                {  std::cout << "foo 2
                ";  }
                
                int main()
                {
                    foo(42);
                    foo(42.);
                }
                

                Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:

                #include <type_traits>
                #include <iostream>
                
                struct foo
                {
                    template<class T, class =
                             typename std::enable_if< std::is_integral<T>::value >::type >
                    foo(T)
                    {  std::cout << "foo(T)
                ";  }
                };
                
                struct bar
                {
                    template<class T, class =
                             typename std::enable_if< not std::is_integral<T>::value >::type >
                    bar(T)
                    {  std::cout << "bar(T)
                ";  }
                };
                
                struct kitty
                {
                    kitty(foo) {}
                    kitty(bar) {}
                };
                
                int main()
                {
                    kitty cat(42);
                    kitty tac(42.);
                }
                

                这篇关于使用 std::function 重载解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

                    <bdo id='FZDnV'></bdo><ul id='FZDnV'></ul>
                    <i id='FZDnV'><tr id='FZDnV'><dt id='FZDnV'><q id='FZDnV'><span id='FZDnV'><b id='FZDnV'><form id='FZDnV'><ins id='FZDnV'></ins><ul id='FZDnV'></ul><sub id='FZDnV'></sub></form><legend id='FZDnV'></legend><bdo id='FZDnV'><pre id='FZDnV'><center id='FZDnV'></center></pre></bdo></b><th id='FZDnV'></th></span></q></dt></tr></i><div id='FZDnV'><tfoot id='FZDnV'></tfoot><dl id='FZDnV'><fieldset id='FZDnV'></fieldset></dl></div>

                      <tfoot id='FZDnV'></tfoot>

                      <small id='FZDnV'></small><noframes id='FZDnV'>

                            <tbody id='FZDnV'></tbody>
                        • <legend id='FZDnV'><style id='FZDnV'><dir id='FZDnV'><q id='FZDnV'></q></dir></style></legend>