【问题标题】:Variadic templates and Tree可变参数模板和树
【发布时间】:2019-04-08 02:02:50
【问题描述】:

我有下一条命令:

auto result = Builder::get(
        ObjKey("key1"),
        ObjVal("test1"),
        (ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22")),
        ObjVal("test2")
    );

我希望得到以下结果:

{key1, test1, {key2, test21, {key3, test31, test32}, test22}, test2}

但我得到了这个结果:

{key1, test1, test22, test2}

程序代码如下(标准11):

#include <iostream>

class ObjKey {
public:
    ObjKey() = delete;

    ObjKey(const std::string& key) : m_key(key) { }

    std::string get() const {
        return m_key + ", ";
    }

private:
    std::string m_key;
};

class ObjVal {
public:
    ObjVal() = delete;

    ObjVal(const std::string& value) : m_value(value) { }

    std::string get() const {
        return m_value + ", ";
    }

private:
    std::string m_value;
};

class Builder {
public:
    template<typename... Args>
    static std::string get(ObjKey&& objKey, Args&& ...args) {
        std::string resultValue;
        get(resultValue, std::forward<ObjKey>(objKey), std::forward<Args>(args)...);
        return resultValue;
    }

private:
    Builder() {}

    template<typename... Args>
    static void get(std::string& resultValue, ObjKey&& objKey, Args&& ...args) {
        resultValue.append("{");
        resultValue.append(objKey.get());
        std::string values;
        get(values, std::forward<Args>(args)...);
        resultValue.append(values);
        resultValue.append("}");
    }

    template<typename... Args>
    static void get(std::string& resultValue, ObjVal&& objVal, Args&& ...args) {
        resultValue.append(objVal.get());
        get(resultValue, std::forward<Args>(args)...);
    }

    static void get(std::string& resultValue) {}
};

int main()
{
    auto result = Builder::get(
        ObjKey("key1"),
        ObjVal("test1"),
        (ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22")),
        ObjVal("test2")
    );
    std::cout << result << "\n";
}

我做错了什么?

用模板可以解决吗?

【问题讨论】:

  • (ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22")))你觉得这个表达式的值是多少?
  • 它是树:ObjKey("key1") ObjVal("test1"), ObjKey("key2") ObjVal("test21") ObjKey("key3") ObjVal("test31") ObjVal ("test32") ObjVal("test22") ObjVal("test2")
  • 我希望你明白它不是一棵树。您的代码中没有任何内容会创建树。我建议创建一个 Tree 类。

标签: c++11 templates tree variadic-templates variadic


【解决方案1】:

我做错了什么?

按照 n.m. 的建议,您必须考虑逗号运算符对以下表达式的作用

(ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22"))

逗号运算符丢弃除最后一个以外的所有值,因此表达式变为

(ObjVal("test22"))

用模板可以解决吗?

是的,但我能想象到的最好的还是std::tuple

我的意思是...如果您接受 get() 调用变为

   auto result = Builder::get(
      ObjKey("key1"),
      ObjVal("test1"),
      std::make_tuple(ObjKey("key2"), ObjVal("test21"),
                      std::make_tuple(ObjKey("key3"), ObjVal("test31"),
                                      ObjVal("test32")),
                      ObjVal("test22")),
      ObjVal("test2"));

所以在分组( 之前加上std::make_tuple,您可以添加一对私有get() 来管理元组大小写。

所以,重命名getH()(用于“get helper”)get() 的私有版本,getH() 的元组管理版本可以写成如下(C++14 解决方案,因为使用 @987654333 @ 和 std::make_index_sequence; 但如果你需要 C++11 的解决方案,写 C++11 的替代品并不难)

  template <std::size_t ... Is, typename ... Ts, typename ... As>
  static void getH (std::string & rV, std::index_sequence<Is...> const &,
                    std::tuple<Ts...> const t, As ... as)
   { 
     getH(rV.append(", "), std::get<Is>(t)...);
     getH(rV, as...);
   }

  template <typename ... Ts, typename ... As>
  static void getH (std::string & rV, std::tuple<ObjKey, Ts...> const t,
                    As ... as)
   { getH(rV, std::make_index_sequence<1u+sizeof...(Ts)>{}, t, as...); }

键值版本管理如下

  template <typename ... As>
  static void getH (std::string & rV, ObjKey const & oK, As ... as)
   {
     getH(rV.append(1u, '{').append(oK.get()), as...);
     rV.append(1u, '}');
   }

  template <typename ... As>
  static void getH (std::string & rV, ObjVal const & oV, As ... as)
   { getH(rV.append(", ").append(oV.get()), as...); }

而且,显然,基础案例

  static void getH (std::string &)
   { }

公众号get()版变

  template <typename ... As>
  static std::string get (As ... as)
   {
     std::string resultValue;
     getH(resultValue, as...);
     return resultValue;
   }

请注意,我已将逗号的管理从 ObjKeyObjVal 转移到 getH(),并且我已删除了转发语义(您从未使用过 std::move)。

以下是完整的 C++14 示例

#include <tuple>
#include <iostream>

class Obj
 {
   public:
      Obj() = delete;

      Obj (std::string const & v0) : v{v0}
       { }

      std::string const & get () const
       { return v; }

   private:
      std::string  v;
 };

struct ObjKey : public Obj
 { ObjKey (std::string const & v0) : Obj{v0} { } };

struct ObjVal : public Obj
 { ObjVal (std::string const & v0) : Obj{v0} { } };

class Builder
 {
   private:
      template <std::size_t ... Is, typename ... Ts, typename ... As>
      static void getH (std::string & rV, std::index_sequence<Is...> const &,
                        std::tuple<Ts...> const t, As ... as)
       { 
         getH(rV.append(", "), std::get<Is>(t)...);
         getH(rV, as...);
       }

      template <typename ... Ts, typename ... As>
      static void getH (std::string & rV, std::tuple<ObjKey, Ts...> const t,
                        As ... as)
       { getH(rV, std::make_index_sequence<1u+sizeof...(Ts)>{}, t, as...); }

      template <typename ... As>
      static void getH (std::string & rV, ObjKey const & oK, As ... as)
       {
         getH(rV.append(1u, '{').append(oK.get()), as...);
         rV.append(1u, '}');
       }

      template <typename ... As>
      static void getH (std::string & rV, ObjVal const & oV, As ... as)
       { getH(rV.append(", ").append(oV.get()), as...); }

      static void getH (std::string &)
       { }

   public:
      template <typename ... As>
      static std::string get (As ... as)
       {
         std::string resultValue;
         getH(resultValue, as...);
         return resultValue;
       }
 };

int main()
 {
   auto result = Builder::get(
      ObjKey("key1"),
      ObjVal("test1"),
      std::make_tuple(ObjKey("key2"), ObjVal("test21"),
                      std::make_tuple(ObjKey("key3"), ObjVal("test31"),
                                      ObjVal("test32")),
                      ObjVal("test22")),
      ObjVal("test2"));

   std::cout << result << "\n";
 }

【讨论】:

  • 我不能使用C+14,也不能提出更难的请求(根据要求):auto result = Builder::get(....
  • @Ivan - 对于 C++11 解决方案,您可以使用 C++11 替换 std::make_index_sequencestd::index_sequence;我建议以下question and answers;对于不是更难的请求......我不明白你的意思。
猜你喜欢
  • 2016-12-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 2013-09-14
  • 2012-03-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多