C++ 具有两个不同类型参数的隐式模板类型推断
假设以下情况: 类型C++ 具有两个不同类型参数的隐式模板类型推断,c++,templates,metaprogramming,template-meta-programming,c++17,C++,Templates,Metaprogramming,Template Meta Programming,C++17,假设以下情况: 类型A和类型B,B可以隐式转换为A,但反之则不成立 我有一个函数 template<class T> void do_stuff(T a, T b); 这里的问题是编译器无法推断类型,而是说: template argument deduction/substitution failed 我可以这样调用我的函数: do_stuff<A>(A{}, B{}); template<class T, class M> void do_stuff
A
和类型B
,B
可以隐式转换为A
,但反之则不成立
我有一个函数
template<class T>
void do_stuff(T a, T b);
这里的问题是编译器无法推断类型,而是说:
template argument deduction/substitution failed
我可以这样调用我的函数:
do_stuff<A>(A{}, B{});
template<class T, class M>
void do_stuff(T a, M b);
do_stuff(A{},B{});
但这对用户来说更烦人
或者,我可以这样做:
do_stuff<A>(A{}, B{});
template<class T, class M>
void do_stuff(T a, M b);
模板
无效数据(T a,M b);
但随后b以其愉快的方式变成了b类型(使用前面的调用)
理想情况下,我想要的是:
template<class T, class M = T>
void do_stuff(T a, M b);
模板
无效数据(T a,M b);
或:
模板
无效的东西(T a,T b);
这样的事情有可能吗?当然有可能,只要有一点授权。通过指定始终希望推断类型是第一个参数的类型,您已经使问题变得非常简单,因此我们所需要做的就是向编译器提供一点提示
template <class T>
void do_stuff_impl(T a, T b) {
cout << "Doing some work..." << endl;
}
template <class T, class S>
void do_stuff(T a, S b) {
do_stuff_impl<T>(a, b);
}
模板
无效执行内容(TA、TB){
coutWrapb
在非推断上下文中。这样,只有a
将被推断,并且b
必须转换为该类型
template <class T> struct dont_deduce { using type = T; };
template <class T> using dont_deduce_t = typename dont_deduce<T>::type;
template<class T>
void do_stuff(T a, dont_deduce_t<T> b);
模板结构不使用type=T;}推导{using};
使用dont_Destruction_t=typename dont_Destruction::type的模板;
模板
无效的做东西(a,不要推断b);
另一种方式,寻求以声明的方式表达意图:
#include <type_traits>
// a B
struct B{};
// an A can be constructed from a B
struct A{
A() {};
A(B) {};
};
// prove that A is constructible from B
static_assert(std::is_convertible<B, A>::value, "");
// enable this function only if a U is convertible to a T
template<
// introduce the actors
class T, class U,
// declare intent
std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr
>
void do_stuff(T, U)
{
}
int main()
{
// legal
do_stuff(A{}, B{});
// does not compile
// do_stuff(B{}, A{});
}
#包括
//a B
结构B{};
//A可以由B构成
结构A{
A(){};
A(B){};
};
//证明A可以由B构造
静态断言(std::is\u convertable::value,“”);
//仅当U可转换为T时启用此功能
模板<
//介绍演员
T类,U类,
//声明意图
std::如果\u t*=nullptr,则启用\u
>
虚无的东西(T,U)
{
}
int main()
{
//合法的
做事(A{},B{});
//不编译
//做东西(B{},A{});
}
更新:
要强制转换,可以使用lambda:
// enable this function only if a U is convertible to a T
template<class T, class U,
std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr
>
void do_stuff(T a, U b)
{
return[](T& a, T b) -> decltype(auto)
{
}(a, b);
}
//仅当U可转换为T时才启用此函数
模板
无效的东西(T a,U b)
{
返回[](T&a,T b)->decltype(自动)
{
}(a、b);
}
在C++11中有答案:std::common\u type
模板
无效f_impl(A、A、b)
{
}
模板
无效f(A、B、B)
{
f_implOk…这实际上是我一直在寻找的答案,但请您解释一下前两行中的黑魔法,或者提供一个参考来解释它。@George See。我们只是防止b
被推导出来,它只是被当作类型为T
的参数。嗯……我不能,我不会不是,但不是decltype(a)一种简单得多的方法?就像在中一样,它不需要理解可推断与不可推断的上下文,也不需要太多代码。我的意思是,我并不是说答案是错的,只是另一个同样有效,值得并排展示side@George我更喜欢更明确,我不喜欢使用decltype()
就像这样。它同样有效——它也创建了一个非推断的上下文——因此,如果你喜欢,一定要这样。@George是的,我肯定——第三次,decltype(a)
和dont\u Destruction\u t
一样是一个未推导的上下文,这就是为什么我一直说它们以相同的方式工作。你强迫a
的类型被推导,然后将其作为b
的类型使用。虽然这实现了我想要的,但我觉得它并不完全合适,因为它仍然允许c为a和b调用不同类型的函数(如果可能的话,我不想允许)do\u stuff\u impl(a,b);
可能更合适。很抱歉,这并不能解决我的问题,也许我在这里表达我的意图不够好:/…在这个例子中,U的类型变成了B。检查确实向我保证类可以转换为A,但我希望它被转换,而不仅仅是保证它可以。想象一下,我有一个方法如果我使用第二个参数(在本例中,类型U变成B)并在“do_stuff”中调用该方法,则在A上使用“do_more_stuff()”“它会生成一个编译错误…所以这不好:/@George updated。添加了一个内部lambda来实际执行转换。这是一个不错的选项,但仍然可以通过委托而不是直接工作。但我可以看到它在许多其他情况下很有用。”。
template<typename A>
void f_impl(A a, A b)
{
}
template<typename A, typename B>
void f(A a, B b)
{
f_impl<typename std::common_type<A, B>::type>(a, b);
}
struct Z
{
};
struct W
{
operator Z();
};
int main()
{
f(1u, 1l); //work
f(W{}, Z{});
f(Z{}, W{}); //and this work too
}