是否可以编写C ++模板来检查构造函数的存在?

时间:2014-10-03 03:09:50

标签: c++ templates visual-studio-2013 sfinae decltype

这个问题本质上是来自另一个用户的这个问题的后续问题,它有一些很好的答案: Is it possible to write a template to check for a function's existence?

我想完全按照这个问题所描述的那样做,除了我希望能够为构造函数做到这一点。例如,鉴于这两种类型:

class NormalType
{
public:
    NormalType()
    {
        std::cout << "NormalType::NormalType()" << std::endl;
    }
};

class SpecialType
{
public:
    SpecialType()
    {
        std::cout << "SpecialType::SpecialType()" << std::endl;
    }
    SpecialType(int someArg)
    {
        std::cout << "SpecialType::SpecialType(int someArg)" << std::endl;
    }
};

这个用于构造对象的辅助函数:

template<class T>
class ConstructHelper
{
public:
    template<bool HasSpecialConstructor>
    static T Construct()
    {
        return T();
    }
    template<>
    static T Construct<true>()
    {
        return T(int(42));
    }
};

我希望能够编写如下代码:

NormalType normalType = ConstructHelper<NormalType>::Construct<has_special_constructor<NormalType>::value>();
SpecialType specialType = ConstructHelper<SpecialType>::Construct<has_special_constructor<SpecialType>::value>();

如果期望的结果是调用NormalType::NormalType(),则调用SpecialType::SpecialType(int someArg)。这里缺少的成分是关键has_special_constructor帮助器,它可以确定我们的特殊构造函数是否存在于给定类型。

我引用的上一个问题涉及检查某个类型上是否存在给定函数,并且提供了各种工作解决方案。不幸的是,它们中的大多数依赖于能够获取目标方法的地址,并且根据C ++规范,您不能获取构造函数的地址(12.1.10)。在剩下的工作解决方案中,所有这些解决方案似乎都依赖于SFINAE和decltype对模板特化中的任意表达式。这是一个解决这个问题的简单方法,但不幸的是我正在使用Visual Studio 2013,它不能正确支持C ++ 11 SFINAE规则,并且仍然不会发布&#34; Visual Studio 14&#34;无论是。有了正确的SFINAE支持,我应该能够做到这一点,例如:

template<class T>
struct has_special_constructor
{
    template<class S>
    struct calculate_value: std::false_type {};
    template<>
    struct calculate_value<decltype(T(int(42)))>: std::true_type {};

    static const bool value = calculate_value<T>::value;
};

但是,如果我尝试测试一种没有定义我的目标构造函数的类型,那么由于缺乏SFINAE支持,它不会在VS2013下编译。也就是说,我还不相信这是不可能的,我认为可能有办法让它发挥作用,但到目前为止我还没能找到解决方案。那里的任何人都看到了我忽略的方法吗?

以下是有关我接受答案作为此问题的解决方案所需要的更多信息:

  • 必须可以针对任何给定类型解析has_special_constructor<T>::value,而无需为该特定类型编写其他代码。

  • 我主要针对Visual Studio 2013,因此任何解决方案都必须在该环境中运行。我知道完全符合C ++ 11的编译器可以更轻松地管理这个问题,但我正在寻找能在我当前的目标编译器上运行的东西。

  • 如果任何人都可以提供在C ++ 03编译器(IE,没有任何C ++ 11功能)的解决方案,我会接受使用C ++ 11功能的解决方案。< / p>

  • 此时我已经满足于基于MSVC扩展的解决方法,因为我可以使用预处理器回退到此方法,直到完全支持C ++ 11。

    < / LI>

1 个答案:

答案 0 :(得分:8)

template<class T>
using has_special_constructor = std::is_constructible<T, int>;

可能的“C ++ 03”版本:

template <class T>
struct has_special_constructor {
  typedef char one;
  typedef struct { char _[2];} two;

  template <std::size_t>
  struct dummy {};

  template<class U>
  static one f(dummy<sizeof(U(42))>*);
  template<class>
  static two f(...);

  static const bool value = sizeof(f<T>(0)) == sizeof(one);
};

这适用于C ++ 03模式下的g ++ 4.4或更高版本。我把“C ++ 03”放在可怕的引号中,因为这取决于表达式SFINAE,seems to be a bit of a gray area in C++03 - 显然C ++ 03的措辞似乎允许它,但没有主要的编译器供应商实际支持它直到C ++ 11几乎就在这里(GCC 4.4于2009年发布),所以“纯粹的”C ++ 03是否允许它是有争议的......