【问题标题】:why C++ template type matching does not match to base class refrence, how can I make it match to base class refrence?为什么 C++ 模板类型匹配与基类引用不匹配,如何使其与基类引用匹配?
【发布时间】:2019-05-27 09:21:02
【问题描述】:

这是我的 C++ 程序代码

#include <iostream>
#include <memory>
using namespace std;

template <typename T>
struct A { T x; };

template <typename T>
struct B:A<T> {  };

template <typename T>
void func(const A<T>& t) {
    cout<<"2"<<endl;
}

template <typename T>
void func(const T& t) {
    cout<<"1"<<endl;
}

int main() {
    B<int> b;
    func(b);
}

它会打印出1。但我期望的是函数调用打印2。为什么B&lt;int&gt;b 匹配const T&amp; 而不是const A&lt;T&gt;&amp;。我怎样才能使它匹配到const A&lt;T&gt;&amp;

【问题讨论】:

  • 对于void func(const T&amp; t)T 推导出为B&lt;int&gt;void func(const B&lt;int&gt;&amp; t)void func(const A&lt;int&gt;&amp; t) 更匹配。
  • 我应该更改什么以使其与void func(const A&lt;int&gt;&amp; t) 匹配?
  • 您可以为每个派生类编写一个代理重载,例如template &lt;typename T&gt; void func(const B&lt;T&gt; &amp;t) {func(static_cast&lt;const A&lt;T&gt; &amp;&gt;(t));}。这听起来有道理吗?
  • 所有派生类是否都遵循相同的模式:template &lt;typename T&gt; struct Derived : A&lt;T&gt;?
  • 谢谢,您的建议在这种情况下会有所帮助。 A&lt;T&gt; 有很多子类。在我的程序中,有很多地方像template&lt;typename T1, typename T2&gt; void func2(const A&lt;T1&gt;&amp;p1, const A&lt;T2&gt;&amp;p2)。如果我使用代理重载,由于笛卡尔积必须编写许多代理重载。

标签: c++ templates matching


【解决方案1】:

解决方案 1

您可以使用std::enable_if 和模板化模板参数来更好地匹配函数,例如:

#include <iostream>
#include <memory>
#include <type_traits>

using namespace std;

template <typename T>
struct A { T x; };

template <typename T>
struct B:A<T> {  };

template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
    cout<<"2"<<endl;
}

template <typename T>
void func(const T& t) {
    cout<<"1"<<endl;
}


int main() {
    B<int> b;
    func(b);
    func(5);
}

但是,这仅在A 采用与T 完全相同的模板参数时才有效。因此,如果您的 B 更改为 ex。

template <typename T, typename U>
struct B : A<T> {}

这将不再起作用。

解决方案 2

基于Yakk's answer,您可以创建更通用的类型特征。此解决方案对其模板参数没有限制,与解决方案 1 一样。

namespace detail 
{
    template <template <typename...> typename Base>
    struct template_base_detector
    {
        template <typename... Args>
        constexpr std::true_type operator()(Base<Args...>*);
        constexpr std::false_type operator()(...);
    };
} 

template <template <typename...> typename Base, typename T>
struct is_template_base_of 
    : decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};

// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;

根据您的 c++ 版本,您可以使用不同的方法来利用此特性。

C++ 17

可能是最紧凑的解决方案。由于 C++ 17 允许使用 constexpr if 语句,这允许我们只定义一个 func:

template <typename T>
void func(const T& t) 
{
    if constexpr (is_template_base_of_v<A, T>)
        cout << 2 << endl;
    else
        cout << 1 << endl;
}

C++ 11 和 14

我们不得不退回到标签调度:

namespace detail 
{
    template <typename T>
    void func(std::true_type, const T& t) 
    {
        std::cout << 2 << endl;
    }

    template <typename T>
    void func(std::false_type, const T& t) 
    {
        std::cout << 1 << endl;
    }
}

template <typename T>
void func(const T& t) 
{
    detail::func(is_template_base_of<A, T>{}, t);
}

【讨论】:

    【解决方案2】:

    标签调度。

    首先,我们编写一个 trait 来检测某事物是否以模板为基础:

    namespace details {
      template<template<class...>class Z>
      struct htb {
        template<class...Ts>
        constexpr std::true_type operator()(Z<Ts...>*){return {};}
        constexpr std::false_type operator()(...){return {};}
      };
    }
    template<template<class...>class Z, class X>
    constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
    

    我们现在可以使用我们的新特性来标记调度:

    namespace details{
      template <typename T>
      void func(std::true_type,const A<T>& t) {
        std::cout<<"2"<<std::endl;
      }
    
      template <class T>
      void func(std::false_type,const T& t) {
        std::cout<<"1"<<std::endl;
      }
    }
    
    template <typename T>
    void func(const T& t) {
      details::func(has_template_base<A,T>,t);
    }
    

    Live example.

    【讨论】:

    • 奇怪的是,当添加另一层时,特定问题的解决方案不起作用 (coliru.stacked-crooked.com/a/c3c9a3b7f5a9460d)。然而,这些特征确实......
    • 我了解类型特征是如何工作的,但是编译器如何在operator() 中推断出Ts...?只看X(来自has_template_base)的类型层次?
    • @Timo 模板参数推导适用于类型及其基本类型?最终,它将A&lt;Ts...&gt;*A&lt;int&gt;* 进行测试,并将{Ts...} 推导出为{int}
    • @StoryTeller 模棱两可有点让人头疼。我发誓我有它的工作,但我现在有问题。我正在取消那个解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 2021-07-30
    相关资源
    最近更新 更多