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
类型的列表)。如何去做 - 这可能是新问题的好候选。
在使用模板类型推导结合 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
类型的列表)。如何去做 - 这可能是新问题的好候选。