代码之家  ›  专栏  ›  技术社区  ›  Sezen

STD:STAT::矢量<派生> STD::向量< BASE >:

  •  1
  • Sezen  · 技术社区  · 7 年前

    首先,标题可能不能反映当前的问题,所以请随意更改。假设我有以下课程;

    #include <iostream>
    #include <vector>
    
    template <typename K, class V>
    class A {
    public:
      K x;
      V y;
      A(K x, V y):x(x), y(y) {}
      void print(A<K, V>& z) {
        std::cout << x + z.x << "-" << y + z.y << std::endl;
      }
      void print(std::vector<A<K,V>> z) {
        for(auto& i:z) {
          print(i);
        }
      }
    };
    
    class B:public A<int, std::string> {
    public:
      B():A(0, "zero") {}
      B(int x, std::string y):A(x, y) {}
    };
    
    void test() {
      B b1(1, "one");
      B b2(2, "two");
      B b3(3, "three");
      B b4(4, "four");
      B b5(5, "five");
      b5.print(b1);
      //
      std::vector<B> c;
      c.push_back(b1);
      c.push_back(b2);
      c.push_back(b3);
      c.push_back(b4);
      b5.print(c);
    }
    

    最后一行我得到了以下错误( b5.print(c) ;

    test_class.cpp:40:6: error: no matching member function for call to 'print'
      b5.print(c);
      ~~~^~~~~
    test_class.cpp:10:8: note: candidate function not viable: no known conversion from 'std::vector<B>' to 'A<int, std::__1::basic_string<char> > &' for 1st argument
      void print(A<K, V>& z) {
           ^
    test_class.cpp:13:8: note: candidate function not viable: no known conversion from 'vector<B>' to 'vector<A<int, std::__1::basic_string<char> >>' for 1st argument
      void print(std::vector<A<K,V>> z) {
           ^
    1 error generated.
    

    我基本上希望 vector<B> std::vector<A<int,std::string>> 但事实并非如此。因此,我提出了两个解决问题的办法。

    1. 定义 typedef std::vector<A<int,std::string>> MyWeirdVector; 在A级使用SE B::MyWeirdVector c; 而不是 std::vector<B> c; .
    2. 将每个打印功能定义为 template <typename U> 在类中接受类型名u作为参数。

    这两种解决方案都有自己的缺点。首先,我必须将c实例化为b::myweirdvector,其次,我(感觉)没有类型安全性。第二种解决方案即使我没有定义类型 <> .

    所以,对于这个问题是否有一个很好的解决方案,比如让隐式类型转换 std::vector<B> STD::向量& lt;a & lt;int,STD::String & Gt; ?

    --编辑--

    感谢@max66和@caleth以及其他同事。我只想分享完整的工作示例。请注意,没有 void 之前 print 如果你不想发疯,@max66的答案。(1)。所有打印函数参数都是 const ,2。合并@max66和@caleth的答案。)

    #include <iostream>
    #include <vector>
    #include <type_traits>
    
    template <typename K, class V>
    class A {
    public:
      K x;
      V y;
      A(K x, V y):x(x), y(y) {}
      void print(const A<K, V>& z) {
        std::cout << x + z.x << "-" << y + z.y << std::endl;
      }
    
      // for C++11, thanks to @Caleth
      // template <typename Container, typename = typename std::enable_if<!std::is_base_of< A<K,V>, typename std::remove_reference<Container>::type >::value>::type>
      // void print(Container&& z) {
      //   for(auto& i:z) {
      //     print(i);
      //   }
      // }
    
      // thanks to @max66
      template <typename T>
      typename std::enable_if<std::is_base_of<A<K, V>, T>::value>::type
        print(std::vector<T> const & z) {
          for(auto const & i:z) print(i);
        }    
      };
    
    class B:public A<int, std::string> {
    public:
      B():A(0, "zero") {}
      B(int x, std::string y):A(x, y) {}
    };
    
    void test() {
      B b1(1, "one");
      B b2(2, "two");
      B b3(3, "three");
      B b4(4, "four");
      B b5(5, "five");
      b5.print(b1);
      //
      std::vector<B> c;
      c.push_back(b1);
      c.push_back(b2);
      c.push_back(b3);
      c.push_back(b4);
      b5.print(c);
    }
    
    3 回复  |  直到 7 年前
        1
  •  3
  •   max66    7 年前

    那怎么办

    template <typename T>
    void print(std::vector<T> const & z) {
      for(auto const & i:z) {
        print(i);
      }
    }
    

    而不是

    void print(std::vector<A<K,V>> z) {
      for(auto& i:z) {
        print(i);
      }
    }
    

    ?

    我的意思是:你不能从 std::vector<B> std::vector<A<K, T>> 但您可以管理泛型的内容 std::vector<T> (通用 T )并从 t 元素到 A<K, T> (如果 t 是派生类型)。

    如果需要,可以添加 std::enable_if 仅在以下情况下启用模板打印功能 T 是从 A<K,T> .

    --编辑--

    手术室问

    我怎么用 STD::EnableαIF 要使模板打印功能仅对派生自的对象执行操作,请执行以下操作:

    有很多方法;通过示例,可以看到caleth的答案,其中包含一个附加的模板类型和 STD::EnableαIF 激活它。

    但我更喜欢由 STD::EnableαIF .

    例如(注意:代码未测试)

    template <typename T>
    typename std::enable_if<std::is_base_of<A<K, V>, T>::value>::type
       print(std::vector<T> const & z)
     { for(auto const & i:z) print(i); }
    

    如果你可以使用C++ 14,你可以简化一点(使用 std::enable_if_t<> 而不是 typename std::enable_if<>::type )

    template <typename T>
    std::enable_if_t<std::is_base_of<A<K, V>, T>::value>
       print(std::vector<T> const & z)
     { for(auto const & i:z) print(i); }
    

    并使用C++ 17多一点( std::is_base_of_v<> 而不是“STD:ISSBaseIOF&L.&G:;:值”

    template <typename T>
    std::enable_if_t<std::is_base_of_v<A<K, V>, T>>
       print(std::vector<T> const & z)
     { for(auto const & i:z) print(i); }
    
        2
  •  1
  •   gsamaras a Data Head    7 年前

    将每个打印功能定义为模板 <typename U>

    与此相反,请只定义引发typename错误的print函数。

    由于这两种类型是完全不同的,隐式转换不是一个选项,但我的建议是。

        3
  •  1
  •   Caleth    7 年前

    为了获得最大的普遍性:

    template<typename Container, typename = std::enable_if_t<!std::is_base_of_v<A<K, V>, std::remove_reference_t<Container>>>>
    void print(Container&& z) {
      for(auto & i : z) {
        print(i);
      }
    }
    

    这个 类型安全。如果您试图传递的内容不是(可能嵌套的)容器 A<K, V> ,模板实例化将失败。