带有两个不同类型参数的隐式模板类型推导

Geo*_*rge 15 c++ templates metaprogramming template-meta-programming c++17

假设以下情况:

类型A和类型B,B可以隐式转换为A但反之则是不真实的.

我有一个功能

template<class T>
void do_stuff(T a, T b);
Run Code Online (Sandbox Code Playgroud)

我想这样调用所述函数:

do_stuff(A{}, B{});
Run Code Online (Sandbox Code Playgroud)

这里的问题是编译器无法推断出类型,而是说:

template argument deduction/substitution failed
Run Code Online (Sandbox Code Playgroud)

我可以这样调用我的函数:

do_stuff<A>(A{}, B{});
Run Code Online (Sandbox Code Playgroud)

但这对用户来说更烦人.

或者我可以做这样的事情:

template<class T, class M>
void do_stuff(T a, M b);
Run Code Online (Sandbox Code Playgroud)

但是b继续以它的快乐方式成为B类(具有先前的调用).

理想情况下,我想要像:

template<class T, class M = T>
void do_stuff(T a, M b);
Run Code Online (Sandbox Code Playgroud)

要么:

template<class T@INSERT MAGIC SO THAT T IS DEDUCED AS BEING THE TYPE OF ARGUMENT NR 1@>
void do_stuff(T a, T b);
Run Code Online (Sandbox Code Playgroud)

这样的事情可能吗?

Bar*_*rry 21

包含b在非推断的上下文中.这样,只会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);
Run Code Online (Sandbox Code Playgroud)

  • @George是的,我敢肯定 - 第三次,`decltype(a)`与`dont_deduce_t <T>`一样是非推导的上下文,这就是为什么我一直说它们的工作方式相同.你强迫推导出"a"的类型,然后只使用它作为`b`的类型. (4认同)
  • @George请参阅[什么是非受限上下文?](/sf/ask/1767181741/).我们只是阻止推导出"b",它只是被视为一个类型为"T"的参数. (3认同)
  • 嗯...我不能不会,但是,不是decltype(a)更简单的方法吗?如同,它不需要理解可推导与不可推导的上下文,也不需要了解如此多的代码.我的意思是我不是说答案是错的,只是说另一个是有效的并且值得并排展示 (3认同)

Yan*_*kes 8

C++ 11中有答案:http:std::common_type //en.cppreference.com/w/cpp/types/common_type

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
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/g/ieuHTS


Sil*_*olo 7

只需要一个小团队,这当然是可能的.通过指定您总是希望推断类型是第一个参数的类型,您已经非常容易地解决了问题,因此我们需要做的就是向编译器稍微提示一下.

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);
}
Run Code Online (Sandbox Code Playgroud)

现在用户可以调用do_stuff任何两个参数,C++将尝试隐式转换第二个参数以匹配第一个参数的类型.如果强制转换无效,您将收到模板实例化错误.它说cannot convert ‘b’ (type ‘A’) to type ‘B’,在海湾合作委员会,这是非常准确和重点.任何有价值的编译器都能够内联该委托调用,因此应该有可忽略不计的开销.