Herb Sutter提出了make_unique()
的简单实现:http://herbsutter.com/gotw/_102/
这是:
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
我的问题是可变参数模板还不是VS2012的一部分,因此我无法按原样使用此代码。
在VS2012中是否有一种可维护的方式来写这个不会涉及使用不同args计数复制粘贴相同函数的方法?
答案 0 :(得分:14)
你可以使用Boost.Preprocessor来生成不同的参数计数,但我真的没有看到它的优点。只需做一次咕噜咕噜的工作,把它塞进标题然后完成。你节省了自己的编译时间,并拥有make_unique
。
Here是我的make_unique.h
标题的复制粘贴,模拟最多5个参数的可变参数模板。
由于OP似乎不喜欢复制粘贴工作,这里是生成上述内容的Boost.Preprocessor代码:
首先,多次创建一个包含模板头的主头(Boost.Preprocessor迭代代码从this answer公然被盗):
// make_unique.h
#include <memory>
#include <utility>
#include <boost/preprocessor.hpp>
#ifndef MAKE_UNIQUE_NUM_ARGS
// allow this to be changed to a higher number if needed,
// ten is a good default number
#define MAKE_UNIQUE_NUM_ARGS 10
#endif
#if MAKE_UNIQUE_NUM_ARGS < 0
// but don't be stupid with it
#error Invalid MAKE_UNIQUE_NUM_ARGS value.
#endif
/* optional, see above for premade version
// include premade functions, to avoid the costly iteration
#include "detail/blah_premade.hpp
// generate classes if needed
#if MAKE_UNIQUE_NUM_ARGS > MAKE_UNIQUE_NUM_PREMADE
*/
#define BOOST_PP_ITERATION_LIMITS (0, MAKE_UNIQUE_NUM_ARGS)
#define BOOST_PP_FILENAME_1 "make_unique_template.h"
#include BOOST_PP_ITERATE()
//#endif
现在制作一个反复包含的模板标题,并根据MAKE_UNIQUE_NUM_ARGS
的值进行不同的扩展:
// make_unique_template.h
// note: no include guard
#define N BOOST_PP_ITERATION()
#define MAKE_UNIQUE_TEMPLATE_PARMS \
BOOST_PP_ENUM_PARAMS(N, typename A)
#define MAKE_UNIQUE_FUNCTION_PARM(J,I,D) \
BOOST_PP_CAT(A,I)&& BOOST_PP_CAT(a,I)
#define MAKE_UNIQUE_FUNCTION_PARMS \
BOOST_PP_ENUM(N, MAKE_UNIQUE_FUNCTION_PARM, BOOST_PP_EMPTY)
#define MAKE_UNIQUE_ARG(J,I,D) \
std::forward<BOOST_PP_CAT(A,I)>(BOOST_PP_CAT(a,I))
#define MAKE_UNIQUE_ARGS \
BOOST_PP_ENUM(N, MAKE_UNIQUE_ARG, BOOST_PP_EMPTY)
template<class T BOOST_PP_COMMA_IF(N) MAKE_UNIQUE_TEMPLATE_PARMS>
std::unique_ptr<T> make_unique(MAKE_UNIQUE_FUNCTION_PARMS){
return std::unique_ptr<T>(new T(MAKE_UNIQUE_ARGS));
}
// clean up
#undef MAKE_UNIQUE_TEMPLATE_PARMS
#undef MAKE_UNIQUE_FUNCTION_PARM
#undef MAKE_UNIQUE_FUNCTION_PARMS
#undef MAKE_UNIQUE_ARG
#undef MAKE_UNIQUE_ARGS
#undef N
答案 1 :(得分:8)
尽管可变参数模板不是VS2012的一部分,但是头文件<memory>
中内置了一些宏来帮助模拟它们。
请参阅this very nice answer,其中显示了如何在几条神秘的线条中实施make_unique<T>
。我确认它运作良好:
#include <memory> // brings in TEMPLATE macros. #define MAKE_UNIQUE(TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \ template<class T COMMA LIST(_CLASS_TYPE)> \ inline std::unique_ptr<T> make_unique(LIST(_TYPE_REFREF_ARG)) \ { \ return std::unique_ptr<T>(new T(LIST(_FORWARD_ARG))); \ } _VARIADIC_EXPAND_0X(MAKE_UNIQUE, , , , ) #undef MAKE_UNIQUE
答案 2 :(得分:1)
使用可变参数列表模拟函数的唯一方法是创建一个合适的重载列表。无论是手动完成,使用类似Boost预处理器库还是使用合适的生成器都是相同的:无法模拟真正的可变参数列表。就个人而言,我认为模拟可变参数列表的最易维护版本是使用一个编译器来支持它们作为预处理器,并让它生成适合编译器编译的代码,而不是支持可变参数模板。
答案 3 :(得分:1)
我已经扩展了Hugues的答案来处理创建包装数组的唯一指针(基本上将它与this paper中的代码合并)并获得以下内容,这在我的VC2012项目中运行良好:
#include <memory>
template<class T> struct _Unique_if {
typedef std::unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]> {
typedef std::unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]> {
typedef void _Known_bound;
};
// Visual Studio 2012 - specific
#define _MAKE_UNIQUE(TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
template<class T COMMA LIST(_CLASS_TYPE)> inline typename _Unique_if<T>::_Single_object make_unique(LIST(_TYPE_REFREF_ARG)) \
{ \
return std::unique_ptr<T>(new T(LIST(_FORWARD_ARG))); \
}
_VARIADIC_EXPAND_0X(_MAKE_UNIQUE, , , , )
#undef _MAKE_UNIQUE
template<class T> inline typename _Unique_if<T>::_Unknown_bound make_unique(size_t n)
{
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[n]());
}
// Visual Studio 2012 - specific
#define _MAKE_UNIQUE(TEMPLATE_LIST, PADDING_LIST, LIST, COMMA, X1, X2, X3, X4) \
template<class T COMMA LIST(_CLASS_TYPE)> typename _Unique_if<T>::_Known_bound make_unique(LIST(_TYPE_REFREF_ARG)) = delete;
_VARIADIC_EXPAND_0X(_MAKE_UNIQUE, , , , )
#undef _MAKE_UNIQUE
答案 4 :(得分:0)
出于好奇,我试图做出类似的事情 - 我需要支持比4更多的参数 - 对于_VARIADIC_EXPAND_0X类型的解决方案 - 可能只支持4个。
这是MacroArg.h头文件:
#pragma once
//
// Retrieve the type
//
// ARGTYPE( (ArgType) argName ) => ArgType
//
// https://stackoverflow.com/questions/41453/how-can-i-add-reflection-to-a-c-application
//
#define ARGTYPE(x) ARGTYPE_PASS2(ARGTYPE_PASS1 x,)
//
// => ARGTYPE_PASS2(ARGTYPE_PASS1 (ArgType) argName,)
//
#define ARGTYPE_PASS1(...) (__VA_ARGS__),
//
// => ARGTYPE_PASS2( (ArgType), argName,)
//
#define ARGTYPE_PASS2(...) ARGTYPE_PASS3((__VA_ARGS__))
//
// => ARGTYPE_PASS2(( (ArgType), argName,))
//
#define ARGTYPE_PASS3(x) ARGTYPE_PASS4 x
//
// => ARGTYPE_PASS4 ( (ArgType), argName,)
//
#define ARGTYPE_PASS4(x, ...) REM x
//
// => REM (ArgType)
//
#define REM(...) __VA_ARGS__
//
// => ArgType
//
//
// This counts the number of args: (0 is also supported)
//
//
// NARGS( (ArgType1) argName1, (ArgType2) argName2 ) => 2
//
#define NARGS(...) NARGS_PASS2(NARGS_PASS1(__VA_ARGS__))
//
// => NARGS_PASS2(NARGS_PASS1( (ArgType1) argName1, (ArgType2) argName2 ) )
//
#define NARGS_PASS1(...) unused, __VA_ARGS__
//
// => NARGS_PASS2( unused, (ArgType1) argName1, (ArgType2) argName2 )
//
#define NARGS_PASS2(...) NARGS_PASS4(NARGS_PASS3(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
//
// => NARGS_PASS4(NARGS_PASS3( unused, (ArgType1) argName1, (ArgType2) argName2 ) , 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) )
//
#define NARGS_PASS3(_unused,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,VAL, ...) VAL
//
// => NARGS_PASS4(2)
//
#define NARGS_PASS4(x) x
//
// => 2
//
//
// Show the type without parenthesis
//
// ARGPAIR( (ArgType1) argName1 ) => ArgType1 argName1
//
#define ARGPAIR(x) REM x
//
// => REM (ArgType1) argName1
//
// => ArgType1 argName1
//
//
// Show the type without parenthesis
//
// ARGPAIR( (ArgType1) argName1 ) => ArgType1 && argName1
//
#define REFARGPAIR(x) REFREM x
//
// => REFREM (ArgType1) argName1
#define REFREM(...) __VA_ARGS__ &&
//
// => ArgType1 && argName1
//
//
// Strip off the type
//
// ARGNAME( (ArgType1) argName1 ) => argName1
//
#define ARGNAME(x) EAT x
//
// => EAT (ArgType1) argName1
//
#define EAT(...)
//
// => argName1
//
//
// This will call a macro on each argument passed in
//
// DOFOREACH(typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 )
//
#define DOFOREACH(macro, ...) DOFOREACH_PASS1(CAT(DOFOREACH_, NARGS(__VA_ARGS__)), (macro, __VA_ARGS__))
//
// => DOFOREACH_PASS1(CAT(DOFOREACH_, NARGS( (ArgType1) argName1, (ArgType1) argName2 ) ), (typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 ) ))
// => DOFOREACH_PASS1(CAT(DOFOREACH_, 2), (typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 ) ))
//
#define CAT(x, y) CAT_PASS1((x, y))
//
// => DOFOREACH_PASS1(CAT_PASS1((DOFOREACH_, 2)), (typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 ) ))
//
#define CAT_PASS1(x) PRIMITIVE_CAT x
//
// => DOFOREACH_PASS1(PRIMITIVE_CAT (DOFOREACH_, 2), (typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 ) ))
//
#define PRIMITIVE_CAT(x, y) x ## y
//
// => DOFOREACH_PASS1( DOFOREACH_2 ), (typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 ) ))
//
#define DOFOREACH_PASS1(m, x) m x
//
// => DOFOREACH_2 (typename ARGTYPE, (ArgType1) argName1, (ArgType1) argName2 ) )
//
#define DOFOREACH_0(m)
#define DOFOREACH_1(m, x1) m(x1)
#define DOFOREACH_2(m, x1, x2) m(x1), m(x2)
//
// => typename ARGTYPE( (ArgType1) argName1 ), typename ARGTYPE( (ArgType1) argName2 ) )
// => typename ArgType1, typename ArgType1
//
#define DOFOREACH_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define DOFOREACH_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define DOFOREACH_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define DOFOREACH_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define DOFOREACH_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define DOFOREACH_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)
#define DOFOREACH_9(m, x1, x2, x3, x4, x5, x6, x7, x8, x9) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9)
#define DOFOREACH_10(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10)
#define DOFOREACH_11(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11)
#define DOFOREACH_12(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12)
#define DOFOREACH_13(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12), m(x13)
#define DOFOREACH_14(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12), m(x13), m(x14)
#define DOFOREACH_15(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12), m(x13), m(x14), m(x15)
//
// Same version as DOFOREACH, except this one meant to be used for appending list of arguments. If 0 arguments, then list is not appended, otherwise additional command is added in front.
//
#define DOFOREACH2(macro, ...) DOFOREACH_PASS1(CAT(DOFOREACH2_, NARGS(__VA_ARGS__)), (macro, __VA_ARGS__))
#define DOFOREACH2_0(m)
#define DOFOREACH2_1(m, x1) ,m(x1)
#define DOFOREACH2_2(m, x1, x2) ,m(x1), m(x2)
#define DOFOREACH2_3(m, x1, x2, x3) ,m(x1), m(x2), m(x3)
#define DOFOREACH2_4(m, x1, x2, x3, x4) ,m(x1), m(x2), m(x3), m(x4)
#define DOFOREACH2_5(m, x1, x2, x3, x4, x5) ,m(x1), m(x2), m(x3), m(x4), m(x5)
#define DOFOREACH2_6(m, x1, x2, x3, x4, x5, x6) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define DOFOREACH2_7(m, x1, x2, x3, x4, x5, x6, x7) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define DOFOREACH2_8(m, x1, x2, x3, x4, x5, x6, x7, x8) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)
#define DOFOREACH2_9(m, x1, x2, x3, x4, x5, x6, x7, x8, x9) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9)
#define DOFOREACH2_10(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10)
#define DOFOREACH2_11(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11)
#define DOFOREACH2_12(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12)
#define DOFOREACH2_13(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12), m(x13)
#define DOFOREACH2_14(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12), m(x13), m(x14)
#define DOFOREACH2_15(m, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) ,m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8), m(x9), m(x10), m(x11), m(x12), m(x13), m(x14), m(x15)
//
// ARGX(1) => (Arg1) arg1
//
#define ARGX(index) (Arg##index) arg##index
//
// Defines same function with different amount of arguments.
//
#define DEFINE_MULTIARG_FUNC(macro) \
macro ( ARGX(1) ); \
macro ( ARGX(1), ARGX(2) ); \
macro ( ARGX(1), ARGX(2), ARGX(3) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12), ARGX(13) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12), ARGX(13), ARGX(14) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12), ARGX(13), ARGX(14), ARGX(15) ); \
//
// Same as previous, except add support also of function with no arguments.
// (template functions normally requires at least one template parameter (so you write template<> in front of function and won't get error), that's why separate define)
//
#define DEFINE_MULTIARG_FUNC2(macro) \
macro ( ); \
macro ( ARGX(1) ); \
macro ( ARGX(1), ARGX(2) ); \
macro ( ARGX(1), ARGX(2), ARGX(3) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12), ARGX(13) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12), ARGX(13), ARGX(14) ); \
macro ( ARGX(1), ARGX(2), ARGX(3), ARGX(4), ARGX(5), ARGX(6), ARGX(7), ARGX(8), ARGX(9), ARGX(10), ARGX(11), ARGX(12), ARGX(13), ARGX(14), ARGX(15) ); \
/*
Here is simple example of usage of these macros:
#define MKFUNC_make_unique(...) \
template <class T DOFOREACH2(typename ARGTYPE, __VA_ARGS__) > \
std::unique_ptr<T> make_unique( DOFOREACH(REFARGPAIR, __VA_ARGS__) ) \
{ \
return std::unique_ptr<T>( new T( DOFOREACH(ARGNAME, __VA_ARGS__) ) ); \
}
DEFINE_MULTIARG_FUNC2(MKFUNC_make_unique); //<--
#undef MKFUNC_make_unique
Debugger will stall in "<--" this line, so it makes sense to keep amount of line absolute minimal.
*/
有关调试宏的一些细节可以在这里找到(正在讨论类似的问题)
答案 5 :(得分:0)
我知道我在这里参加派对已经迟到了,但我刚刚遇到过这个。我一直在使用单线程宏(并且与VS2012兼容):
#define MAKE_UNIQUE(T, ...) std::unique_ptr<T>(new T(__VA_ARGS__))
或者不太便携,但你可以传递空参数
#define MAKE_UNIQUE(T, ...) std::unique_ptr<T>(new T(##__VA_ARGS__))
像这样使用:
// MAKE_UNIQUE takes the type followed by the arguments list ...
std::unique_ptr<MyClass> pPointer = MAKE_UNIQUE(MyClass, param_1, param_2, ..., param_n);