C++14 元组类型索引在从 std::bind 推断的类型上失败,我想成为 std::function

C++14 tuple type indexing fails on inferred type from std::bind that I want to be std::function

在使用模板类型推导结合 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() 来解决这个问题?

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

要实现你想要的 - 你需要 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 类型使用变体函数,其中 "non convertible version" 将为空:

    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);
   }

最后一条评论 - 您可能会尝试从您的元组类型中 "retrieve" 当前类型(我的意思是这些 Mx 类型的列表)。如何去做 - 这可能是新问题的好候选。