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

Implicit template type deduction with two arguments of different types

假设以下情况:

类型A和类型BB可以隐式转换为A,反之则不正确。

我有一个功能

template<class T>
void do_stuff(T a, T b);

我想这样调用所述函数:

do_stuff(A{}, B{});

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

template argument deduction/substitution failed

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

do_stuff<A>(A{}, B{});

但这对用户来说更烦人。

或者我可以这样做:

template<class T, class M>
void do_stuff(T a, M b);

但随后 b 继续其快乐的方式成为 B 类型(通过先前的调用)。

理想情况下我想要这样的东西:

template<class T, class M = T>
void do_stuff(T a, M b);

或:

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);

这样的事情可能吗?

这当然是可能的,只是需要一点授权。通过指定您始终希望推断类型为第一个参数的类型,您已经使问题变得非常简单,因此我们需要做的就是向编译器提供一点提示。

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);
}

现在用户可以使用任意两个参数调用 do_stuff,C++ 将尝试隐式转换第二个参数以匹配第一个参数的类型。如果转换无效,您将收到模板实例化错误。在 GCC 上,它表示 cannot convert ‘b’ (type ‘A’) to type ‘B’,非常准确而且切题。任何称职的编译器都能够内联该委托调用,因此开销应该可以忽略不计。

在非推导上下文中包装 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);

另一种方式,试图以声明的方式表达意图:

#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{});

}

更新:

要强制转换,可以使用 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);
}

C++11 中有答案:std::common_type http://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
}

https://godbolt.org/g/ieuHTS