简体   繁体   中英

Conditional class template constructor

I am trying to make a class that has conditional members as below (sample code to illustrate problem):

template<bool b>
struct conditional_members {};

template<>
struct conditional_members<true> 
{ int m; };

template<typename T, bool b>
struct my_class : public conditional_members<b>
{
    T n;

    // constructor for case when b is false
    my_class(T n) : n(n) {};

    // constructor for case when b is true
    my_class(T n, int m) : n(n), m(m) {};
};

I need to have two conditional constructors depending on the bool b but this does not compile. I tried specializing the constructors with bool value:

template<typename T>
my_class<T, true>::my_class(T n, int m) : n(n), m(m) {};

template<typename T>
my_class<T, false>::my_class(T n) : n(n) {};

but that doesn't compile either because partial function template specializations are not allowed.

Is there a way to achieve this?

The problem with

// constructor for case when b is true
my_class(T n, int m) : n(n), m(m) {};

is that a constructor's mem-initializer-list can name only virtual base classes, direct base classes, and direct non-static data members, but never an inherited member like m . This is because the member of a base class is initialized by the base class subobject constructor, so it can't be initialized again (though it could be assigned).

You can instead specify the base class initializer. With this example, conditional_members is an aggregate, so aggregate initialization will work:

// constructor for case when b is true
my_class(T n, int m) : n(n), conditional_members<b>{m} {};

Though with just that, you might get some strange side effects from the fact that my_class specializations always have the two constructors declared, even if it might be invalid to actually instantiate one constructor or the other.

Here's an SFINAE trick to make the constructors conditionally effectively invisible, depending on b :

#include <type_traits>

// Define conditional_members as before.

template<typename T, bool b>
class my_class : public conditional_members<b>
{
    T n;

public:
    // constructor for case when b is false
    template <typename = std::enable_if_t<!b>>
    my_class(T n) : n(n) {}

    // constructor for case when b is true
    template <typename = std::enable_if_t<b>>
    my_class(T n, int m) : conditional_members<b>{m}, n(n) {}
};

As a preview, with C++20 constraints, you'll be able to write it this nice, simpler way instead:

template<typename T, bool b>
class my_class : public conditional_members<b>
{
    T n;

public:
    // constructor for case when b is false
    my_class(T n) requires(!b) : n(n) {}

    // constructor for case when b is true
    my_class(T n, int m) requires(b) : conditional_members<b>{m}, n(n) {}
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM