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

时间:2017-06-29 20:27:14

标签: c++ templates metaprogramming template-meta-programming c++17

假设以下情况:

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

这样的事情可能吗?

4 个答案:

答案 0 :(得分: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);

答案 1 :(得分:8)

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

答案 2 :(得分:7)

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

let newObject = {
  configuration: {
    '11111-2222-3333-444--5555': o.configuration['11111-2222-3333-444--5555']
  }
}

现在用户可以使用任意两个参数调用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); } ,C ++将尝试隐式转换第二个参数以匹配第一个参数的类型。如果演员表无效,您将获得模板实例化错误。在海湾合作委员会,它说do_stuff,这是非常准确和重点。任何有价值的编译器都能够内联委托调用,因此开销应该可以忽略不计。

答案 3 :(得分:1)

另一种方式,寻求以声明方式表达意图:

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