【发布时间】:2014-06-30 13:36:55
【问题描述】:
我一直在探索 Visual Studio 2013 Pro 中 C++ 11 的许多新功能。到目前为止,我不得不说这很糟糕,但我在研究中遇到了一个障碍,我希望我能举起 mu 手并获得一些帮助。 (这个网站的忠实粉丝,第一次发布,如果代码设置不正确,请见谅)。
目前,我正在尝试创建一个将函数和/或成员函数 (operator ()) 绑定到 std::function. 的封装实例的通用方法
我正在使用可变参数模板参数来处理可变参数列表,通过可变参数的大小绘制参数的数量,并使用编译时递归中的值来抽取/返回正确数量的占位符进入绑定操作。
因为std::bind 需要一个实例而不是一个类型,所以编译时递归助手只是返回std::_Ph <N> 的实例。我使用帮助器的特殊化(std::_Ph<1>)作为分隔符,否则它只会变成一个无限循环,在编译期间淹没调用堆栈。
这是我正在使用的体验代码示例:
//Nothing special, just a polymorphic base
class iObject {
public:
virtual ~iObject () {};
};
/*Again nothing special, just a default null object implementation of an object that overloads operator ()*/
template < typename RETURN_TYPE, typename ... ARGS >
class BaseCall : public iObject {
public:
~BaseCall () {};
virtual RETURN_TYPE operator () ( ARGS ... args ) {
return 0;
};
};
//One more bit of testing fodder, a concrete implementation of BaseCall
template < typename RETURN_TYPE, typename ... ARGS >
struct SampleMax : public BaseCall < RETURN_TYPE, ARGS ... > {
private:
/*deals with error C2903: 'result' : symbol is neither a class template not a function template*/
typedef RETURN_TYPE result_type;
//Inverted template method + trailing return
protected:
//Function assumes we are working with a sorted STL container
template < typename FIRST_ARG >
inline auto Implementation ( FIRST_ARG & Sample ) -> result_type {
return * ( Sample.rbegin() );
};
public:
/*For all public operator () members like this the code would be the same
result_type operator () ( ARGS ... args ) {
return Implementation ( args ... );
};
};
//My compile-time helpers
//Got the idea from here:
//http://stackoverflow.com/questions/8759872/compile-time-recursion-and-conditionals
template < int N >
std :: _Ph < N > * CTRecursivePlaceholder () {
return new std :: _Ph < N - 1 >;
};
//Specialization
template <>
std :: _Ph < 1 > * CTRecursivePlaceholder < 1 > () {
return new std :: _Ph < 1 >;
};
//Here's the beef
template < typename RETURN_TYPE, typename ...ARGS >
class Experiment {
public: /*Ideally private, but for testing purposes it doesn't really matter right now*/
std :: function < RETURN_TYPE ( ARGS ... ) Receiver;
public:
Experiment () {
Receiver = std :: bind ( BaseCall < RETURN_TYPE, ARGS ... > (), * ( CTRecursivePlaceholder < ( sizeof ... ( ARGS ) ) > () ) );
};
virtual ~Experiment () {};
};
问题#1:在只需要一个参数的情况下,创建实例正常工作,以及绑定和调用独立函数:
/*pretend a vector of doubles called vec exists so I don't have to write it ;)*/
//...somewhere in a header...
double func ( std :: vector < double > & k )
{ return 1.0f; };
//...somewhere in a source file...
Experiment < double, std :: vector < double > & > exp; //<-- instantiates just fine
exp.Receiver = & func; //works
exp.Receiver ( vec ); //works
但是,当我尝试将 std::function 数据成员分配给 BaseCall 的子代时,例如我的 SampleMax 仿函数,如下所示:
SampleMax < double, std :: vector < double > & > max;
exp.Reciever = & max; //no go
我收到error C2064: term does not evaluate to a function taking 1 arguments。错误源自外部参照包头内部的 ,。
如果我在 Experiment 构造函数中直接使用占位符,它会起作用。
问题 #2:全面(独立函数和成员函数),如果我尝试创建一个带有两个或更多参数签名的 Experiment 实例,如下所示:
//...somewhere in a source file...
Experiment < double, std :: vector < double > &, std :: vector < double > & > exp;
我收到以下错误 (C2440):'return' : cannot convert from 'std :: _Ph < 1 > *' to 'std :: _Ph < 2 > *'
如果我没记错的话,编译器总是优先考虑特化而不是模板定义,所以它可能是在特化而不是模板定义之后。
我也不太确定 std::_Ph 的模板定义中的 'N - 1' 语句,因为它应该在创建实例之前执行算术,但这就是示例运行的方式所以我也是这样做的。
请原谅任何错字,我在手机上写了整个内容,感谢您提供的任何帮助!
【问题讨论】:
-
std::_Ph?听起来不便携。 Here's a portable solution -
我不太明白为什么
CTRecursivePlaceholder使用new,返回一个指针,以及打算在哪里进行递归。 -
据我所知,指向
&max之类的对象的指针是不可调用的。max本身是可调用的,但exp.Receiver = max将复制max。 OTOH,exp.Receiver = std::ref(max)将存储对max的引用。 -
_Ph这个名字既没有出现在官方的 C++11 标准中,也没有出现在 n3797(相当新的 C++1y 草案)中。它很可能是您正在使用的标准库实现的实现细节。 “我必须返回该类型的一个实例,因此是新的” 和 “如果我试图返回一个本地实例,它将超出范围。” Basic C++ 101: 值语义。您可以传递和返回按值。new不是必需的。 -
std::_Ph是 Microsoft/Dinkumware 标准库实现的实现细节的一部分。尝试使用 g++/libstdc++ 或 clang++/libc++ 编译您的代码,您会注意到它们没有任何std::_Ph。这就是它不便携的原因。另一方面,std::placeholders::_1等是标准的,这意味着您可以保证在标准库的任何实现中都有它们。
标签: generics c++11 recursion compile-time