首页 > 解决方案 > 模板参数不能在隐式构造的参数上推导出来

问题描述

我想在 c++17 中有以下代码:

#include <iostream>
#include <string>
#include <type_traits>
#include <functional>

class Foo;

template<class T>
class Bar {
public:

    std::function<T(Foo&)> m_fn;

    template<class Fn>
    Bar(Fn fn) : m_fn(fn) {};

    T thing(Foo &foo) const {
        return m_fn(foo);
    }
};


template<class Fn>
Bar(Fn) -> Bar<decltype(std::invoke(std::declval<Fn>(),
                                    std::declval<Foo&>()))>;

class Foo {
public:
    Foo() {};

    template<class T>
    std::vector<T> do_thing(const Bar<T> &b) {
        std::vector<T> r;

        r.push_back(b.thing(*this));

        return r;
    }
};


std::string test(Foo &) {
    return "hello";
}

int main() {
    Foo foo = Foo();

    // works
    std::vector<std::string> s = foo.do_thing(Bar{test});
    
    // cant deduce T parameter to do_thing
    std::vector<std::string> s = foo.do_thing({test});
}

但是编译这个给了我“无法在调用do_thing.如果可能,将其作为参数传递。do_thing(Bar{test})do_thing({test})do_thing(test)Bar

我也不想转发声明要传递给do_thing任何一个的变量

有什么方法可以指导模板参数的推断,T以便调用do_thing可以保持干净?

编辑:

抱歉编辑晚了,但在我包含的示例中,Bar 构造函数的参数过于简化。实际上,有一个额外的参数std::optional<std::string> desc = std::nullopt,将来可能会改变(尽管不太可能)。所以建造Bar内部do_thing会有点难以维护......

标签: c++templatesc++17template-argument-deduction

解决方案


想要拥有do_thing({test})do_thing(test)隐式构造 aBar并将其作为参数传递(如果可能)。

不幸的是,当您调用do_thing({test})or时do_thing(test)test(或{test})不是Bar<T>对象。所以编译器无法推断T类型,也无法构造Bar<T>对象。

一种先有鸡还是先有蛋的问题。

我能想象的最好Foodo_test()方法是在 中添加如下方法

template<typename T>
auto do_thing (T const & t)
 { return do_thing(Bar{t}); } 

这样你就可以调用(没有图表)

std::vector<std::string> s = foo.do_thing(test);

你得到相同的结果

std::vector<std::string> s = foo.do_thing(Bar{test});

- 编辑 -

OP问

有什么方法可以保留 {test} 大括号语法吗?也许与 initializer_list 或什么?

是的...与std::initializer_list

template<typename T>
auto do_thing (std::initializer_list<T> const & l)
{ return do_thing(Bar{*(l.begin())}); }

但是,这样,你也接受

std::vector<std::string> s = foo.do_thing(Bar{test1, test2, test3});

仅使用test1

也许更好一点……另一种方法是通过 C 风格的数组

template <typename T>
auto do_thing (T const (&arr)[1])
 { return do_thing(arr[0]); }

这样你只接受一个元素。


推荐阅读