3

我在下面给出一个例子。该程序编译并运行良好,但我想知道它是否是根据 C++11 标准理论上未定义的行为;我可以返回绑定(临时)本地函数对象的结果吗?

示例代码(简称编辑):

#include <iostream>
#include <functional>

struct MyFunctionObject
{
    inline void operator() ( const std::string& name )
        { std::cout<< "Hello " << name << "!" << std::endl; }
};

std::function< void () > my_greeter( const std::string& name )
{
    MyFunctionObject fun;
    return std::bind( fun, name );
}

int main()
{
    auto g = my_greeter("sheljohn");
    g();
}

编辑原始示例:

#include <random>
#include <functional>
#include <algorithm>
#include <utility>
#include <iostream>



        /********************     **********     ********************/
        /********************     **********     ********************/



namespace foo {


/**
 * Type of the generator returned by the method 'bind' below.
 */
template <class _dist>
using generator_type = std::function< typename _dist::result_type () >;

// ------------------------------------------------------------------------

/**
 * Wrapper for C++11 random library.
 */
template <
    class _engine = std::mt19937_64,
    class _device = std::random_device
>
struct random
{
    typedef _engine engine_type;
    typedef _device device_type;
    typedef random<_engine,_device> self;

    template <class _dist>
    static inline generator_type<_dist>
    bind( _dist& distribution )
        { return std::bind( distribution, self::engine ); }

    template <class _dist>
    static inline generator_type<_dist>
    bind( _dist&& distribution )
        { return std::bind( distribution, self::engine ); }

    static engine_type engine;
    static device_type device;
};

// Initialize static engine
template <class _engine,class _device>
_device random<_engine,_device>::device;

template <class _engine,class _device>
_engine random<_engine,_device>::engine = _engine( device() );

// ------------------------------------------------------------------------

/**
 * Generic binder to standard random distributions.
 *
 * SO QUESTION: does this cause undefined behaviour?
 */

template <class _dist, class... Args>
constexpr generator_type<_dist> random_generator( Args&&... args )
    { return random<>::bind( _dist( std::forward<Args>(args)... ) ); }


}; // namespace: foo



        /********************     **********     ********************/
        /********************     **********     ********************/



int main()
{
    auto ngen = foo::random_generator< std::normal_distribution<double> >( 0.0, 1.0 );

    for(unsigned i=0; i<10; ++i) 
        std::cout<< ngen() << " " << std::endl;
}
4

2 回答 2

2

该标准实际上在第 26.5 节中有一个关于bind()使用随机数生成器调用的特定信息:

[注意:这些实体的指定方式允许将任何均匀随机数生成器对象 e 作为参数绑定到任何随机数分布对象 d,从而产生一个零参数函数对象,例如由 bind(d ,e)。——尾注]

所以你正在做的事情是明确允许的。

同样来自[func.bind.bind],描述指出,当您调用它时bind(F&& f, BoundArgs&&... bound_args)(强调我的):

  • FD是类型decay_t<F>
  • fd是一个类型的左值,FDstd::forward<F>(f)
  • Ti是模板参数包中的第 i 个类型BoundArgs
  • TiD是类型decay_t<Ti>
  • ti是函数参数包中的第 i 个类型bound_args
  • tid是一个类型的左值,TiDstd::forward<Ti>(ti)
  • ..

返回:转发呼叫包装器g...的效果g(,u1, u2, ..., uM)应为INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(V2), ... std::forward<VN>(vN), result_of_t<FD cv & (V1, V2, ..., VN)>)

该部分对我来说有点令人困惑,但基本上你传入的仿函数和参数被转发(复制/移动)到本地版本,fd并且tid.... bind不会保留对仿函数的引用(FD不是引用类型!),所有参数也是如此(的类型也不引用类型)。因此,在您的新示例中,即使作为两者的引用的字符串超出范围,结果仍然是有效的可调用对象。TiDBoundArgsfunnamebind()

这里没有UB。

于 2015-01-15T12:16:07.290 回答
0

你没有 UB,但你真的有更多的运气而不是理性。

这里没有未定义的行为,只是因为这里:

return std::bind( distribution, self::engine );

您按值传递“分布”,这意味着传递的是它的副本(尽管分布是可变引用)。由于不能在指定位置为同一类型的值和引用重载函数,因此在您想要这样做的函数中必须存在各种技巧 - 就是这样:bind() 需要std::reference_wrapper -包装类型以传递引用。因此,如果您这样做,您肯定会有未定义的行为:

return std::bind( ref(distribution), self::engine );

可以在这里找到。在这种情况下,您将传递一个真正的 bind 引用,在这种情况下,它是对传递给包含它的函数外部的局部变量的引用。

于 2015-01-15T12:02:29.643 回答