6

This question is in spirit a follow-on from this question from another user, which has some excellent answers: Is it possible to write a template to check for a function's existence?

I want to do exactly what is described in this question, except I want to be able to do it for a constructor. Eg, given these two types:

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;
    }
};

And this helper function for constructing an object:

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

I want to be able to write code like this:

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

Where the desired results are that NormalType::NormalType() is called, and SpecialType::SpecialType(int someArg) is called. The missing ingredient here is that critical has_special_constructor helper, which can determine if our special constructor exists for a given type.

The previous question I referenced deals with checking if a given function exists on a type, and there were a variety of working solutions presented. Unfortunately, most of them rely on being able to take the address of the target method, and as per the C++ spec, you can't take the address of a constructor (12.1.10). Of the remaining working solutions, all of them seem to rely on SFINAE with decltype on an arbitrary expression in a template specialization. That is a simple way to solve this problem, but unfortunately I'm working on Visual Studio 2013, which doesn't properly support the C++11 SFINAE rules, and still won't with the release of "Visual Studio 14" either. With proper SFINAE support, I should be able to do this for example:

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;
};

But that won't compile under VS2013 if I try and test a type that doesn't define my target constructor, due to the lack of SFINAE support. That said, I'm not yet convinced this is impossible, I think there might be a way to make it work, but I haven't been able to find a solution so far. Anyone out there see a way to do this that I've overlooked?

Here's some more info about what I'd need in order to accept an answer as a solution to this problem:

  • It must be possible to resolve has_special_constructor<T>::value for any given type without additional code being written for that specific type.

  • I'm primarily targeting Visual Studio 2013, so any solution must work in that environment. I know a fully conforming C++11 compiler could manage this problem more easily, but I'm looking for something that can function now on my current target compiler.

  • If anyone can supply a solution that works in a C++03 compiler (IE, without any C++11 features) I'll accept that over one that uses C++11 features.

  • I'd settle for a MSVC extension-based workaround at this point, since I can use the preprocessor to fall back to this method until full C++11 support comes along.

Community
  • 1
  • 1
Roger Sanders
  • 2,232
  • 1
  • 22
  • 29

1 Answers1

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

Possible "C++03" version:

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);
};

This works on g++ 4.4 or later in C++03 mode. I put "C++03" is in scary quotes since this depends on expression SFINAE, which seems to be a bit of a gray area in C++03 - apparently C++03's wording seemed to allow it, but no major compiler vendor actually supported it until C++11 is almost here (GCC 4.4 was released in 2009), so it's arguable whether "pure" C++03 allows it...

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Hah! Simple as that! I need to go through the list of C++11 type traits again and commit them to memory I think. I just spent 3 hours looking for a solution to this problem. – Roger Sanders Oct 03 '14 at 03:25
  • For bonus points* (well, not really, but I would if I could!) any idea if std::is_constructible could be emulated in a C++03 compiler? – Roger Sanders Oct 03 '14 at 03:28
  • @RogerSanders See my edit. I believe you need expression SFINAE for this, which is *kind of* C++03, I guess. – T.C. Oct 03 '14 at 03:56
  • Thanks for the C++03 example, no luck with it in Visual Studio though, even in 2013. I made a few attempts at modifying it, but I either got a compiler error or the wrong result with everything I tried. I'm pretty sure now without better SFINAE support there's no way to do this without C++11 type traits. C++03 support isn't essential for my problem though. Thanks, you've been a big help. – Roger Sanders Oct 03 '14 at 05:06
  • @RogerSanders Even Visual Studio 2013 [doesn't support](http://msdn.microsoft.com/ru-ru/library/hh567368.aspx) expression [SFINAE](http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error). – Constructor Oct 03 '14 at 11:03