Lærne
Lærne

Reputation: 3142

Why is template type deduction failing here?

Why in the following piece of code, does the template type cannot be deduced automatically from the last argument, like it does in std::condition_variable::wait ?

template< typename Predicate >
    //requires Truth< Predicate >
class lock_monitor_guard
{
public:
    lock_monitor_guard( std::mutex& mutex, std::condition_variable& monitor, Predicate predicate );
    ~lock_monitor_guard();

private:
    std::unique_lock<std::mutex> lock;
    std::condition_variable& monitor;
};

template< typename Predicate >
    //requires Truth< Predicate >
lock_monitor_guard<Predicate>::lock_monitor_guard( std::mutex& mutex, std::condition_variable& monitor, Predicate predicate )
    : lock( mutex ), monitor( monitor )
{
        monitor.wait<Predicate>( lock, predicate );
}

template< typename Predicate >
    //requires Truth< Predicate >
lock_monitor_guard<Predicate>::~lock_monitor_guard()
{
        lock.unlock();
        monitor.notify_one();
}

When I try to build a line like lock_monitor_guard guard( jobs_mutex, jobs_monitor, ([]()->bool{return true;}) );, I have an error message : use of class template 'lock_monitor_guard' requires template arguments. But why ? And why does it work with the STL std::condition_variable::wait. Thank you for any help !

Upvotes: 1

Views: 392

Answers (2)

cdhowie
cdhowie

Reputation: 168998

Template arguments for types cannot be deduced, period. This is because type specializations can provide a completely different set of members, so until you know the type's template arguments you don't even know what constructors are available.

This is why the standard library has helper functions, like std::make_pair. When constructing or referring to an std::pair you must specify the type arguments (std::pair<int, double>(1, 2.0)) but in most cases they can be deduced when calling a function (std::make_pair(1, 2.0)). (Note that std::condition_variable::wait is a function, not a type.)

If you implement a move constructor for your lock_monitor_guard type then you can create a helper function similar in spirit to std::make_pair and then use copy-initialization with auto to provide deduction. (Note that you'll also have to tweak your destructor to account for the possibility that this had its contents "stolen" by the move constructor.)

template <typename Predicate>
lock_monitor_guard<Predicate> create_lock_monitor_guard(
    std::mutex & mutex,
    std::condition_variable & monitor,
    Predicate && predicate)
{
    return lock_monitor_guard<Predicate>(mutex, monitor, std::forward<Predicate>(predicate));
}

auto guard = create_lock_monitor_guard(
    jobs_mutex,
    jobs_monitor,
    [] { return true; });

Upvotes: 6

Jagannath
Jagannath

Reputation: 4025

You can pass the type as shown below.

template< typename Predicate >
        //requires Truth< Predicate >
    class lock_monitor_guard
    {
    public:
        lock_monitor_guard( std::mutex& mutex, std::condition_variable& monitor, Predicate predicate );
    };


auto pred = [] { return true; };

lock_monitor_guard<decltype(pred)> l(jobs_mutex, jobs_monitor, pred);

And std::condition_variable::wait is function and not type hence the type is automatically deduced.

Upvotes: 2

Related Questions