【问题标题】:Template argument deduction fails on C++14模板参数推导在 C++14 上失败
【发布时间】:2022-01-06 08:14:01
【问题描述】:

我试图编译此代码,但它在 C++14 上失败,而它在 C++17 上运行

#include <cstdio>
#include <utility>

template <typename F>
struct S {
    explicit S(F&& fn): fn(std::move(fn)) {}
    F fn;
    ~S() { fn(); }
};


int main(){
    S obj([]() noexcept {
        std::printf("Foo\n");
    });                                                                                                 
}

我在C++17中的see thatS的构造函数被调用为

   call    S<main::{lambda()#1}>::S(main::{lambda()#1}&&)

这表明编译器推导出了模板参数。除了执行以下操作之外,还有其他方法可以在 C++14 中编译此代码吗?

auto fn = []() noexcept {
    std::printf("Foo\n");
};
S<decltype(fn)> obj(std::move(fn)); 

【问题讨论】:

  • 代码依赖于 C++17 特性。没有办法将其构建为未更改的 C++14。
  • @StoryTeller-UnslanderMonica 我明白了。您能否指出您所指的具体功能?
  • CTAD。从字面上看,您正在尝试做的事情。

标签: c++ templates


【解决方案1】:

类模板参数推导(CTAD)仅在 C++17 中引入。你可以用函数推导出参数:

template <typename F>
S<F> make_S(F&& fn) { return S<F>{std::forward<F>(fn)}; }

int main(){
    auto obj = make_S([]() noexcept {
        std::printf("Foo\n");
    });
}

【讨论】:

  • 请注意,这与 c++17 语义略有不同。该类不会隐式接受左值,而这会。
  • 另外,需要S 才能允许复制/移动。
【解决方案2】:

如果您可以使用固定的函数签名,那么这也适用于 C++14。

#include <functional>
#include <iostream>
#include <utility>

struct S 
{
    template <typename F>
    explicit S(F&& fn) : 
        m_fn(fn)
    {
    }

    ~S() 
    { 
        m_fn(); 
    }

private:
    std::function<void ()> m_fn;
};


int main() {
    S obj([]() noexcept 
    {
            std::cout << "Foo\n";
    });

    return 0;
}

【讨论】:

  • 您甚至可以通过将[fn] () { fn(); } 分配给m_fn 而不是直接分配fn 来抽象出函数的返回值。
  • 你不再需要模板构造函数,explicit S(std::function&lt;void()&gt; fn) 就足够了。
  • @Jarod42 做得好
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-28
  • 2014-09-22
  • 2011-07-28
  • 1970-01-01
  • 2012-02-07
  • 1970-01-01
相关资源
最近更新 更多