【问题标题】:C++14 tuple type indexing fails on inferred type from std::bind that I want to be std::functionC++14 元组类型索引在从 std::bind 推断的类型上失败,我想成为 std::function
【发布时间】:2016-01-05 19:42:13
【问题描述】:

将模板类型推导与 C++14 std::get 与类型索引结合使用时出现错误。代码可能看起来有点复杂,但我已尝试将其缩减为所发生事情的基本情况。它实际上只是一个观察者模式......结构'A'允许根据消息类型(M1,M2,......)设置观察者。请注意,为了简单起见,每种消息类型只有一个观察者。

现在诀窍(以及失败的部分)是使用 C++14 的 std::get,它允许您使用实际类型索引到唯一类型的元组。这是一个简单的例子来说明我的意思:

void sample()
{
    std::tuple<int, float> myTuple;
    std::get<float>(myTuple) = 3.141f;  // C++14 allows this
    std::get<1>(myTuple) = 3.141f;      // C++11 way to do it, using index
}

考虑到这一点,这是我的程序(与上面的代码分开)因为 C++14 元组类型索引在推断类型上失败而无法编译:

#include <cxxabi.h>
#include <stdlib.h>
#include <functional>
#include <vector>
#include <tuple>
#include <typeinfo>
#include <iostream>
#include <string>

// ===================================
// A quick'n'dirty way to print types (nonportable)
// And yes, I know this code could be improved :)
inline
std::string demangle(char const *mangled)
{
   char *output = (char *)malloc(16384);
   size_t length = 16384;
   int status;

   __cxxabiv1::__cxa_demangle(mangled, output, &length, &status);
   std::string s(output, length);
   free(output);
   return s;
}

#define DEMANGLE(T) demangle(typeid(T).name())

// ===================================
struct A
{
   struct M1
   {};

   struct M2
   {};

   using Tuple = std::tuple<
        std::function<void(M1 const &)>
       ,std::function<void(M2 const &)>
   >;

   template<typename T>
   void setObserver(T func)
   {
      // This works fine
      std::cout << DEMANGLE(T) << std::endl;

      // ************************************************
      // The line below does not compile (std::get fails)
      //
      // Note the type of T prints out as:
      // std::_Bind<std::_Mem_fn<void (B::*)(A::M1 const&)> (B*, std::_Placeholder<1>)>
      //
      // Rather than the (desired):
      // std::function<void (A::M1 const&)>(A::M1 const&)> (B*, std::_Placeholder<1>)>
      //
      // ************************************************
      std::get<T>(tuple_) = func; // C++14 only
   }

private:
   Tuple tuple_;
};

// ===================================
struct B
{
   void func(A::M1 const &)
   {}
};

// ===================================
int main()
{
   A *a = new A;
   B *b = new B;

   using namespace std::placeholders;

   a->addObserver(std::bind(&B::func, b, _1));

   return 0;
}

更新:

建议的解决方案确实解决了从 std::bind(...) 转换为 std::function(...) 的问题,但它要求我为我的每种类型都有一个单独的 setObserver() 函数M1,M2,...

如何模板化 setObserver() 来解决这个问题?

【问题讨论】:

    标签: c++ templates tuples c++14 stdbind


    【解决方案1】:

    std::bind()不返回std::function,但某些未指定的类型可转换为std::function&lt;&gt;。其次,C++14 形式的std::get&lt;&gt;() 需要精确类型,而不仅仅是可转换为元组成员类型之一的任何内容。

    为了实现你想要的 - 你需要 convert 你的 T 到你的元组类型之一。

    例如您可以将当前的 setObserver 移动到私有部分并重命名 - 并为您想要的类型创建函数:

       template<typename T>
       auto setObserver(T func) 
          -> typename std::enable_if<std::is_convertible<T, std::function<void(M1 const &)>>::value>::type 
       {
           this->template setObserverImpl<std::function<void(M1 const &)>>(func);
       }
       template<typename T>
       auto setObserver(T func) 
          -> typename std::enable_if<std::is_convertible<T, std::function<void(M2 const &)>>::value>::type 
       {
           this->template setObserverImpl<std::function<void(M2 const &)>>(func);
       }
    
    private: 
       template<typename T>
       void setObserverImpl(T func)
       {
         // no change here
       }
    

    除非你只有两种类型,否则你需要将当前的 setObserver 设为模板函数。

    要模板化此解决方案 - 为每个 Mx 类型使用变体函数,其中“不可转换版本”将为空:

        template <typename T, typename M>
        auto setObserverTemplate(T func) -> typename std::enable_if<std::is_convertible<T, std::function<void(M const &)>>::value>::type 
        {
           this->template setObserverImpl<std::function<void(M const &)>>(func);  
        }
        template <typename T, typename M>
        auto setObserverTemplate(T) -> typename std::enable_if<not std::is_convertible<T, std::function<void(M const &)>>::value>::type 
        {
           // do nothing
        }
    
       template<typename T>
       auto setObserver(T func)
       {
           this->template setObserverTemplate<T,M1>(func);
           this->template setObserverTemplate<T,M2>(func);
       }
    

    或者,可能更好 - 带有标记empty 函数的可变参数版本:

       template<typename T, typename MFirst, typename ...M>
       auto setObserverVariadic(T func)
       {
           this->template setObserverTemplate<T,MFirst>(func);
           this->template setObserverVariadic<T,M...>(func);
       }
       template<typename T>
       auto setObserverVariadic(T)
       {
       }
    
       template<typename T>
       auto setObserver(T func)
       {
           this->template setObserverVariadic<T,M1,M2>(func);
       }
    

    最后一条评论 - 您可能会尝试从您的元组类型中“检索”当前类型(我的意思是 Mx 类型的这些列表)。怎么做 - 这可能是新问题的好人选。

    【讨论】:

    • 我试图避免为每个 M1、M2 设置单独的 setObserver() 函数……这就是我要模板化的原因。有什么方法可以只使用一个 setObsrver() 模板函数,而不是一个用于 M1 和一个用于 M2?
    猜你喜欢
    • 2020-01-02
    • 1970-01-01
    • 1970-01-01
    • 2019-12-02
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    • 2015-11-11
    • 2021-09-20
    相关资源
    最近更新 更多