Joseph Franciscus
Joseph Franciscus

Reputation: 371

Template Meta programming - Determining function parameter

template<bool, class T, class U> 
struct IF_ELSE { using type = T; };

template<class T, class U>
struct IF_ELSE<false, T, Y> { using type = U; }

template<class T>
class Object { 

  Object baz(IF_ELSE<boolean_condition<T>::value, foo_class, bar_class>::type param) {

       return Object(param);
   }
};

Hello I am trying to create a method that returns an object that is initialized based upon some template conditional. The code above works fine however I would like to use a default constructor (no parameters)

template<class T>
class Object { 

  Object baz(IF_ELSE<boolean_condition<T>::value, foo_class, void>::type param) {

       return Object(param);
   }
};

However it is not possible to use void as a type (despite void foo(void); being a valid decleration)

Is there anyway to do this?

Notes. I DO NOT WANT TO USE TEMPLATE SPECIALIZATION. Although this is a solution for this specific question it would be inconvenient simply use specialization in my current project. Personally I prefer the usage of IF_ELSE opposed to specialization as I find it much more comprehensible.

The only workaround I can think of is something like...

template<class T>
class Object { 

  Object baz(IF_ELSE<boolean_condition<T>::value, foo_class, int>::type param = 0) {
        if (param == 0)
       return Object();
        else 
       return Object(param);

   }
};

If anyone has a more sophisticated solution that would be great. Much thanks.

----------------------------------EDITED------------------------------

This workaround is a bit nicer (inspired by Oisyn) it more or less combines the best of both worlds of parameter specialization and default parameters. Possibly a bit faster too as it dodges the if statement.

template<class T>
class Object
{

public:
struct MAKE_ILLEGAL {};

template<class param>
Object(param p) {/*do stuff */}
Object() {/* default */ }


template<bool b>
Object<T> baz(std::conditional_t<b, int, MAKE_ILLEGAL> param)
{ return Object<T>(param); }

template<bool b>
Object<T> baz(std::conditional_t<b, MAKE_ILLEGAL, int> value = 0)
{ return Object<T>(); }
};

int main() {

    Object<double> obj;
    obj.baz<false>();
}

Upvotes: 0

Views: 124

Answers (1)

oisyn
oisyn

Reputation: 1376

EDIT Apparently you're not allowed to use something that resolves to void as a function argument in a type-dependent context. I've edited my answer accordingly using a different solution. The original answer can be read below.

// A helper that always yields true so we can make the condition type-dependent
// on arbitrary template parameters
template<class T>
constexpr bool true_v = true;

template<class T>
class Object
{
public:
    template<class U = void, std::enable_if_t<Condition<T>::value && true_v<U>, int> = 0>
    Object baz(foo_class param)
    { return Object(param); }

    template<class U = void, std::enable_if_t<!Condition<T>::value && true_v<U>, int> = 0>
    Object baz()
    { return Object(); }
};

As long as we're not dealing with special methods like copy/move ctors and copy/move assignment operators (which may not be templatized), we can always apply regular SFINAE tricks. We just need to templatize baz using default template arguments, and make sure the condition used for std::enable_if is type-dependent on a template parameter of that method (and not solely on T in Object<T>)

Live version


Original answer

template<class T>
class Object
{
private:
    struct dummy { };

public:
    Object baz(std::conditional_t<Conditional<T>::value, foo_class, dummy> param)
    { return Object(param); }

    Object baz(std::conditional_t<Conditional<T>::value, dummy, void>)
    { return Object(); }
};

The idea is that you create both overloads, one for a single parameter and one for none. By selectively replacing the parameter with an inaccessible dummy type, you can make sure you will never be able to call that overload when not appropriate.

Upvotes: 2

Related Questions