Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有两个不同类型参数的隐式模板类型推断_C++_Templates_Metaprogramming_Template Meta Programming_C++17 - Fatal编程技术网

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

coutWrap
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);
模板结构不使用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
}