Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to write a C++ template to check for a constructor's existence?

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.

like image 952
Roger Sanders Avatar asked Oct 03 '14 03:10

Roger Sanders


People also ask

Can you template a constructor?

As long as you are satisfied with automatic type inference, you can use a template constructor (of a non-template class). @updogliu: Absolutely. But, the question is asking about "a template constructor with no arguments" If there are no function arguments, no template arguments may be deduced.

Can we use template in C?

The main type of templates that can be implemented in C are static templates. Static templates are created at compile time and do not perform runtime checks on sizes, because they shift that responsibility to the compiler.

What is a template function?

Function templates are similar to class templates but define a family of functions. With function templates, you can specify a set of functions that are based on the same code but act on different types or classes. The following function template swaps two items: C++ Copy.

What is the correct form of declaring a template in C++?

Class Template Declarationtemplate <class T> class className { private: T var; ... .. ... public: T functionName(T arg); ... .. ... }; In the above declaration, T is the template argument which is a placeholder for the data type used, and class is a keyword.


1 Answers

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...

like image 54
T.C. Avatar answered Oct 06 '22 00:10

T.C.