Search code examples
c++c++11undefined-behaviorfunction-object

Does returning a bound local function object cause undefined behaviour?


I am giving an example below. The program compiles and runs fine, but I am wondering whether it is theoretically undefined behaviour according to the C++11 standard; can I return the result of binding a (temporary) local function object?

Example code (edited for short):

#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();
}

EDIT Original example:

#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;
}

Solution

  • The standard actually has a specific bit about calling bind() with a random number generator, in §26.5:

    [ Note: These entities are specified in such a way as to permit the binding of any uniform random number generator object e as the argument to any random number distribution object d, thus producing a zero-argument function object such as given by bind(d,e). —end note ]

    So what you're doing is explicitly allowed for.

    Also from [func.bind.bind], the description states that when you call bind(F&& f, BoundArgs&&... bound_args) that (emphasis mine):

    • FD is the type decay_t<F>,
    • fd is an lvalue of type FD constructed from std::forward<F>(f)
    • Ti is the ith type in the template parameter pack BoundArgs
    • TiD is the type decay_t<Ti>
    • ti is the ith type in the function parameter pack bound_args
    • tid is an lvalue of type TiD constructed from std::forward<Ti>(ti)
    • ..

    Returns: A forwarding call wrapper g... The effect of g(,u1, u2, ..., uM) shall be INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(V2), ... std::forward<VN>(vN), result_of_t<FD cv & (V1, V2, ..., VN)>)

    The section is a little confusing to me, but basically the functor and the arguments you pass in is forwarded (copied/move) into local version, fd and tid.... bind won't keep a reference to the functor (FD is not a reference type!), and the same is true of all of the arguments (the TiD types for the BoundArgs are not reference types either). So in your new example, even if fun and the string that name is a reference to both go out of scope, the bind() result is still a valid callable.

    There is no UB here.