【问题标题】:Making a tuple of PoD from a tuple of Strong Types从一个强类型元组中创建一个 PoD 元组
【发布时间】:2018-03-27 19:59:45
【问题描述】:

我有一个 StrongType<> 类在 PoD 上强制执行强类型:

template <typename T, typename TAG>
class StrongType {
private:
    std::string mName;
    T mValue;
public:
    explicit StrongType(std::string n) : mName(n), mValue() {}
    explicit StrongType(std::string n, T v)
        : mName(n), mValue(std::move(v)) {
    }

    const T &value() const { return mValue; }
    std::string name() const { return mName; }
}; 

我有一个类,它保留了一个 StrongTypes 的元组,并且需要返回一个非强类型元组(实际上它应该调用一个具有非强类型参数的函数):

struct aTag {};
struct bTag {};
struct cTag {};

using aType = utils::StrongType<int, aTag>;
using bType = utils::StrongType<std::string, bTag>;
using cType = utils::StrongType<int, cTag>;

int main()
{
    aType a("a", 2);
    bType b("b", std::string {"b"});
    cType c("c", 10);

    AdvTuple<aType,bType,cType> t(a,b,c);

    //auto nt = t.getTuple();
    //std::cout << std::tuple_size<decltype(nt)>() << "\n";
    //std::cout << std::get<0>(nt) << "\n";

    //nt.call([](aType ra, bType rb, cType rc) {
    //});

    return 0;
}

这是我做的实现,但它没有编译:

#include <tuple>

template <typename ...T>
class AdvTuple {
private:
    std::tuple<T...> aTuple;
public:
    explicit AdvTuple(T... ts)
        : aTuple(std::make_tuple(ts...)) {
    }

    template <int i>
    decltype(std::get<i>(aTuple).value()) get() const {
        return std::get<i>(aTuple).value();
    }

    template <int N = 0, typename ...TA, std::enable_if<N < sizeof...(TA)> >
    auto getImpl(std::tuple<TA...> t) {
        return std::tuple_cat(std::make_tuple(std::get<N>(t)), getImpl<N+1>(t));
    };

    template <typename ...Q>
    std::tuple<Q...> getTuple() const {
        return getImpl<0>(aTuple);
    }
};

这是来自编译器的消息(mac 上的 clang):

 In file included from
 /Users/happycactus/Documents/Progetti/Experiments/tupletraits/main.cpp:3:
 /Users/happycactus/Documents/Progetti/Experiments/tupletraits/tupletypes.h:32:16: error: no matching member function for call to 'getImpl'
        return getImpl<0>(aTuple);
               ^~~~~~~~~~

 /Users/happycactus/Documents/Progetti/Experiments/tupletraits/main.cpp:25:17: note: in instantiation of function template specialization 'AdvTuple<utils::StrongType<int, aTag>, utils::StrongType<std::__1::basic_string<char>, bTag>, utils::StrongType<int, cTag> >::getTuple<>' requested here
    auto nt = t.getTuple();
              ^

 /Users/happycactus/Documents/Progetti/Experiments/tupletraits/tupletypes.h:26:10: note: candidate template ignored: couldn't infer template argument ''
    auto getImpl(std::tuple<TA...> t) {
         ^
 1 error generated.

1) 如何解决?

2) 如何使用 lambda / function&lt;&gt; 和推导 PoD 类型来实现 call() 函数?即使不是StrongTyped 也可以。

我可以使用 C++11 和 14。

【问题讨论】:

  • getTuple&lt;Q...&gt; -- 在呼叫站点应该从哪里获得Q...?我不清楚 getTuple 应该返回什么,来自包含的强类型的 T 副本的元组?
  • 您对std::enable_if 的使用是错误的,永远无法推断出非类型参数。 std::enable_if_t&lt;N &lt; sizeof...(TA), int&gt; = 0

标签: c++ c++11 tuples c++14 variadic-templates


【解决方案1】:

如果我理解正确,您需要std::make_index_sequencestd::index_sequence

我的意思是......而不是递归getImpl(),您可以使用包扩展如下

  template <std::size_t ... Is>
  auto getTuple (std::index_sequence<Is...> const &) const
   { return std::make_tuple(std::get<Is>(aTuple).value()...); }

  auto getTuple () const
   { return getTuple(std::make_index_sequence<sizeof...(T)>{}); }

您可以将private 设为第一个getTuple()(接收std::index_sequence 的那个)。

关于call(),我不清楚你想要什么。但我想你想要一个AdvTuplecall() 方法,它接收一个可调用对象并使用value() 调用它。

再次使用std::make_index_sequence/std::index_sequence技巧,就像

  template <typename F, std::size_t ... Is>
  auto call (F f, std::index_sequence<Is...> const &) const
   { return f(std::get<Is>(aTuple).value()...); }

  template <typename F>
  auto call(F f) const
   { return call(f, std::make_index_sequence<sizeof...(Ts)>{}); }

第一个可以是private

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

#include <tuple>
#include <string>
#include <iostream>
#include <type_traits>

template <typename T, typename>
class StrongType
 {
   private:
      std::string mName;
      T mValue;

   public:
      explicit StrongType (std::string n) : mName{std::move(n)}, mValue{}
       { }

      template <typename U>
      explicit StrongType (std::string n, U v)
         : mName{std::move(n)}, mValue{std::forward<U>(v)}
       { }

      T const & value () const
       { return mValue; }

      std::string const & name () const
       { return mName; }
 };

template <typename ... Ts>
class AdvTuple
 {
   private:
      std::tuple<Ts...> aTuple;

      template <std::size_t ... Is>
      auto getTuple (std::index_sequence<Is...> const &) const
       { return std::make_tuple(std::get<Is>(aTuple).value()...); }

      template <typename F, std::size_t ... Is>
      auto call (F f, std::index_sequence<Is...> const &) const
       { return f(std::get<Is>(aTuple).value()...); }

   public:
      template <typename ... Us>
      explicit AdvTuple (Us && ... us)
         : aTuple{std::make_tuple(std::forward<Us>(us)...)}
       { }

      template <std::size_t i>
      auto get() const
       { return std::get<i>(aTuple).value(); }

      auto getTuple () const
       { return getTuple(std::make_index_sequence<sizeof...(Ts)>{}); }

      template <typename F>
      auto call(F f) const
       { return call(f, std::make_index_sequence<sizeof...(Ts)>{}); }
 };

struct aTag {};
struct bTag {};
struct cTag {};

using aType = StrongType<int, aTag>;
using bType = StrongType<std::string, bTag>;
using cType = StrongType<int, cTag>;

int main ()
 {
   aType a("a", 2);
   bType b("b", std::string {"b"});
   cType c("c", 10);

   AdvTuple<aType,bType,cType> t(a,b,c);

   auto nt = t.getTuple();
   std::cout << std::tuple_size<decltype(nt)>() << "\n";
   std::cout << std::get<0>(nt) << "\n";

   t.call([](int, std::string, int){ std::cout << "Lambda!" << std::endl; });

   return 0;
 }

【讨论】:

  • 哦,谢谢,它很有魅力,确实解决了我所有的问题。您对call(Ts...) 的实现正是我所需要的。我猜由于缺少自动返回类型推导,它不能向后移植到C++11。还是谢谢你!
  • @HappyCactus - 首先,抱歉:我忘记公开const call();答案更正。第二:不,缺少自动返回类型并不是将解决方案反向移植到 C++11 的真正问题:您可以使用 auto funcName (...) -&gt; decltype(...) 语法; template &lt;typename F&gt; auto call (F f) const -&gt; decltype(call(f, std::make_index_sequence&lt;sizeof...(Ts)&gt;{})) 之类的东西;它既可怕又多余,但它与 C++11 兼容。第三:真正的 C++11 反向移植问题是 std::index_sequencestd::make_index_sequence,仅从 C++14 开始可用(继续)
  • @HappyCactus -(继续)但是为它们编写一个符合 C++11 的替代品并不难。如果你愿意,我可以添加我的解决方案的 C++11 版本。
猜你喜欢
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多