C++中的部分类模板参数推导



我有一个模板化的类,但只有一部分模板参数可以从构造函数中推导出来。

在调用构造函数时,有没有办法在尖括号内提供其余的模板参数?

假设我们使用的是C++17。

template<typename T1, typename T2>
struct S
{
T2 t2;
S(const T2& _t2) : t2{_t2} {}
void operator()(const T1& t1)
{
std::cout << t1 << ", " << t2 << 'n';
}
};
int main()
{
S<int, double> s {3.14};
std::function<void(int)> func = s;

func(42);
// What I want:
//S<int> s2 {3.14};     <- T1 is provided in the angle brackets, T2 is deduced
//std::function<void(int)> func2 = s;
//func2(42);
}

据我所知,我们需要在尖括号中提供所有模板参数,或者不提供它们,并使用CTAD。问题是,我不想写所有的模板参数(在我的实际用例中,大约有5-6个,它们非常冗长(,但我也不想在构造函数中传递所有的参数,因为其中一些参数不用于构造对象。我只需要运算符((方法的类型。

我无法将operator((方法模板化,因为我想将其绑定到std::函数对象,并且在绑定过程中无法推导模板参数类型。这就是为什么我需要包装类中的所有类型。

函数存在此部分模板推导。

例如:

template<typename T1, typename T2>
void foo(const T2& t2)
{
T1 t1{};
std::cout << t1 << ", " << t2 << 'n';
}
int main()
{
foo<int>(3.4); //T1 is explicitly int, T2 is deduced to be double
}

我目前的解决方案是利用这个功能,并通过一个函数构造对象:

template<typename U1, typename U2>
S<U1, U2> construct_S(const U2& t2)
{
return S<U1, U2>{t2};
}
int main()
{
auto s2 = construct_S<int>(1.5);
std::function<void(int)> func2 = s2;
func2(23);
}

我觉得这个解决方案很笨拙,因为我们使用了一个外部函数来构造对象。

我想知道是否有更清洁的解决方案。

也许有一些演绎指南?我不确定。

如注释中所述,您可以使用嵌套类,以便可以单独提供两个参数(一个明确地提供,另一个推导(:

template<typename T1>
struct S {
template <typename T2>
struct impl {
T2 t2;    
impl(const T2& _t2) : t2{_t2} {}
};
template <typename T2>
impl(const T2&) -> impl<T2>;
};

int main() {
S<int>::impl<double> s {3.14};
S<int>::impl s2 {3.14};    // <- T1 is provided in the angle brackets, T2 is deduced
}

我发现了这个如何为嵌套模板类提供推导指南?。尽管如此,上面的代码编译时没有出现gcc和clang的问题:https://godbolt.org/z/MMaPYGbe1.

如果重构类模板不是一个选项,那么helper函数是一个通用且干净的解决方案。标准库有许多make_xxx函数,其中一些函数是在CTAD出现之前才需要的。

最简单的方法是提供工厂函数,它将接管类型的推导:

template<typename T1, typename T2>
auto makeS(T2 x) -> S<T1, T2>
{
return S<T1, T2>{x};
}

https://godbolt.org/z/4cPTdv7e3

template<typename T2>
struct S
{
T2 t2;
S(const T2& _t2) : t2{_t2} {}
template<typename T1>
void operator()(const T1& t1)
{
std::cout << t1 << ", " << t2 << 'n';
}
};
int main()
{
S s {3.14}; // T1 not provided
std::function<void(int)> func2 = s; // T1 deduced by std function
func2(42); // works
}

我刚刚删除了T1,并将其作为operator()的模板参数,一切都正常。

活生生的例子。

最新更新