【问题标题】:Why is template type deduction failing here?为什么模板类型推导在这里失败?
【发布时间】:2014-12-30 19:13:33
【问题描述】:

为什么在下面的代码中,不能像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();
}

当我尝试构建像lock_monitor_guard guard( jobs_mutex, jobs_monitor, ([]()-&gt;bool{return true;}) ); 这样的行时,我收到一条错误消息:use of class template 'lock_monitor_guard' requires template arguments。但为什么 ?以及为什么它可以与 STL std::condition_variable::wait 一起使用。感谢您的帮助!

【问题讨论】:

  • 你必须给 lock_monitor_guard 的模板类型。
  • 是的,但是为什么呢?我不想猜测 lambda 的可怕类型!为什么std::condition_variable::wait 不同?
  • 您可以移动template&lt; typename Predicate &gt; 使其成为构造函数声明的一部分,然后它将起作用。整个lock_monitor_guard 类不需要使用Predicate 的类型进行参数化
  • 非常真实的 piotr。这是最好的解决方法。

标签: c++ templates c++11 condition-variable type-deduction


【解决方案1】:

不能推断类型的模板参数,句号。这是因为类型特化可以提供一组完全不同的成员,所以在您知道类型的模板参数之前,您甚至不知道有哪些构造函数可用。

这就是标准库有辅助函数的原因,比如std::make_pair。在构造或引用 std::pair 时,您必须指定类型参数 (std::pair&lt;int, double&gt;(1, 2.0)),但在大多数情况下,它们可以在调用函数时推断出来 (std::make_pair(1, 2.0))。 (注意std::condition_variable::wait 是一个函数,而不是一个类型。)

如果你为你的lock_monitor_guard 类型实现了一个移动构造函数,那么你可以创建一个在本质上类似于std::make_pair 的辅助函数,然后使用带有auto 的复制初始化来提供推论。 (请注意,您还必须调整析构函数以说明 this 的内容被移动构造函数“窃取”的可能性。)

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

【讨论】:

  • 当类型被推导时,编译器不能在所有可能特化的所有构造函数中进行选择吗?
  • @Lærne 可以更改语言以允许这样做吗?可能。但是标准不允许,所以不会发生。此外,您还会遇到有人引入新的专业化并且您的代码中断或行为不同的情况,因为突然推断要么模棱两可,要么导致选择与您预期不同的类型。就目前而言,在检查构造函数之前必须知道类型。为了推断,你需要已经推断出来了!鸡和蛋。
  • @Lærne 当然。前提是您知道如何编写能够在有限的时间和内存中评估无限数量的可能实例化的编译器。
  • @James 因为有有限多的书面特化和构造函数,所以要进行的检查也是有限的。您不需要实际评估所有潜在的实例化,只要有不同的专业化规则乘以规则的构造函数数即可。但我承认复杂性在这里可能是一个问题。 cdhowie 的破解代码问题也是有效的。我个人会补充说这是一个愚蠢的想法,因为当有可用的专业化版本时,您最终可能会使用非专业化模板,因为您使用仅在非专业化模板中定义的构造函数。
  • @Lærne 问题是,如果你写类似TemplateType( init_value ) 的东西,编译器必须验证TemplateType 的所有可能实例化,只有一个可以用init_value 构造.对于简单的情况,这很可能可以推断出来(除了可能有明确的特化),但总的来说,我认为它不能在有限的时间或内存中解决。
【解决方案2】:

您可以传递如下所示的类型。

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);

std::condition_variable::wait 是函数而不是类型,因此会自动推断类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-27
    • 2011-03-19
    • 2017-06-22
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多