有没有办法在VS2012中编写make_unique()?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有没有办法在VS2012中编写make_unique()?相关的知识,希望对你有一定的参考价值。

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计数复制粘贴相同的函数?

答案

你可以使用Boost.Preprocessor来生成不同的参数计数,但我真的没有看到它的优点。只需做一次咕噜咕噜的工作,把它塞进标题然后完成。你节省了自己的编译时间并拥有你的make_unique

Here是我的make_unique.h标题的复制粘贴,模拟最多5个参数的可变参数模板。


由于OP似乎不喜欢复制粘贴工作,这里是生成上述内容的Boost.Preprocessor代码:

首先,多次创建一个包含模板头的主头(从this answer公然窃取Boost.Preprocessor迭代代码):

// 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
另一答案

尽管可变参数模板不是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
另一答案

使用可变参数列表模拟函数的唯一方法是创建一个合适的重载列表。无论是手动完成,使用类似Boost预处理器库还是使用合适的生成器都是相同的:无法模拟真正的可变参数列表。就个人而言,我认为模拟可变参数列表的最易维护版本是使用一个编译器来支持它们作为预处理器,并让它生成适合编译器编译的代码,而不是支持可变参数模板。

另一答案

我已经扩展了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 - 对于_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_PASS

以上是关于有没有办法在VS2012中编写make_unique()?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 VS2012 中使用 sdk 6.1 远程构建 Xamarin.iOS?

VS2012程序打包部署详解

如何用vs2012编写能在windows2000运行的程序

VS2010编写的代码在VS2012中打开,中文全部乱码

有没有办法从 VS 中的调试器中转储流

VS2012在一个解决方案中发布多个项目