Mathieu Van Nevel
Mathieu Van Nevel

Reputation: 1486

Overloading members from template class on bool value

I'm trying to avoid concepts on the master branch of my projects, so I need some kind of alternative with type_trait:

I need a class whom some few functions will change depending on the bool value.

Someone already suggest me to split my class, but this case won't make sense here. For some context it's a pool where remove function and some others will change if the object type of the pool can be shared or not.

So I tried to use std::enable_if, but there are still some errors (I want the declaration and the implementation to be separate).

#include  <type_traits>

template < typename Object, bool Shared = false >
class Foo {

  template < bool S = Shared, typename std::enable_if<S>::type* = nullptr >
  void   bar();

  template < bool S = Shared, typename std::enable_if<!S>::type* = nullptr >
  void   bar();
};

template < typename Object,
           bool Shared >
template < bool S, typename std::enable_if<S>::type* = nullptr >
void   Foo<Object, Shared>::bar() {
    //do something
}


template < typename Object,
           bool Shared >
template < bool S, typename std::enable_if<!S>::type* = nullptr >
void   Foo<Object, Shared>::bar() {
  //do nothing
}

int main() {

  Foo<int> test;
  return 0;
}

Test3.cpp:16:33: error: default argument for template parameter for class enclosing ‘void Foo<Object, Shared>::bar()’
 void   Foo<Object, Shared>::bar() {
                                 ^
Test3.cpp:24:33: error: default argument for template parameter for class enclosing ‘void Foo<Object, Shared>::bar()’
 void   Foo<Object, Shared>::bar() {

EDIT: Removed copy/paste error

Upvotes: 3

Views: 1480

Answers (4)

Jarod42
Jarod42

Reputation: 217358

As alternative, you might use tag-dispatching:

template <typename Object, bool Shared = false>
class Foo {
public:
    void bar() { bar(std::integral_constant<bool, Shared>{}); }

private:
    void bar(std::true_type);
    void bar(std::false_type);
};

template <typename Object, bool Shared>
void Foo<Object, Shared>::bar(std::true_type) { /**/ }

template <typename Object, bool Shared>
void Foo<Object, Shared>::bar(std::false_type) { /**/ }

Since C++17, you might use if constexpr:

template <typename Object, bool Shared = false>
class Foo {
public:
    void bar() {
        if constexpr (Shared) {
            // ...
        } else {
            // ...
        }
    }

So each block is only available in the right version:

  • so no runtime branch (I expect regular branch to be optimized anyway in your case)
  • not chosen block may contain code valid only when that branch would be chosen (mostly useful when it depends of type).

And C++20 introduces requires:

template <typename Object, bool Shared = false>
class Foo {
public:
    void bar() requires(Shared) {
        // ...
    }
    void bar() requires(!Shared) {
        // ...
    }
};

Upvotes: 2

ABu
ABu

Reputation: 12269

As extra note, that is a much more cleaner solution:

#include <type_traits>
#include <iostream>

template<bool b>
using allow_if = typename std::enable_if<b>::type;

template <typename Object, bool Shared = false>
class Foo {
public:
    template<bool S = Shared>
    allow_if<S> bar();

    template<bool S = Shared>
    allow_if<!S> bar();
};

template<typename Object, bool Shared>
template<bool S>
allow_if<S> Foo<Object, Shared>::bar() {
    std::cout << "do something" << std::endl;
}

template<typename Object, bool Shared>
template<bool S>
allow_if<!S> Foo<Object, Shared>::bar() {
    std::cout << "do nothing" << std::endl;
}

int main() {
    Foo<int> test;
    test.bar<>();
    test.bar<true>();
    return 0;
}

Upvotes: 1

songyuanyao
songyuanyao

Reputation: 172934

1.The qualification of class name (i.e. Foo<Object, Shared>::) should be deleted for member function declaration inside the class definition.

2.Default template arguments are not allowed for out-of-class definition of member templates, just remove them.

Default parameters are not allowed

  • in the out-of-class definition of a member template (they have to be provided in the declaration inside the class body)

Then

template < typename Object, bool Shared = false >
class Foo {

  template < bool S = Shared, typename std::enable_if<S>::type* = nullptr >
  void  bar();

  template < bool S = Shared, typename std::enable_if<!S>::type* = nullptr >
  void  bar();
};

template < typename Object,
           bool Shared >
template < bool S, typename std::enable_if<S>::type*  >
void   Foo<Object, Shared>::bar() {
    //do something
}

template < typename Object,
           bool Shared >
template < bool S, typename std::enable_if<!S>::type* >
void   Foo<Object, Shared>::bar() {
  //do nothing
}

LIVE

Upvotes: 4

skypjack
skypjack

Reputation: 50550

Here is your code once fixed (minimal, working example):

#include  <type_traits>
#include <iostream>

template < typename Object, bool Shared = false >
class Foo {
public:
    template < bool S = Shared, typename std::enable_if<S>::type* = nullptr >
    void bar();

    template < bool S = Shared, typename std::enable_if<!S>::type* = nullptr >
    void bar();
};

template < typename Object, bool Shared >
template < bool S, typename std::enable_if<S>::type*>
void  Foo<Object, Shared>::bar() {
    std::cout << "do something" << std::endl;
}

template < typename Object, bool Shared >
template < bool S, typename std::enable_if<!S>::type*>
void Foo<Object, Shared>::bar() {
    std::cout << "do nothing" << std::endl;
}

int main() {
    Foo<int> test;
    test.bar<>();
    test.bar<true>();
    return 0;
}

It was a problem of extra qualifiers and default parameters not to be repeated in out of class definitions.

Upvotes: 1

Related Questions