Jon Deaton
Jon Deaton

Reputation: 4379

How to enable member function using boolean template parameter?

I would like a class to have two different implementations of push, and choose based on a boolean template argument. I tried using the SFINAE principle as described in this answer, like so:

template<class T, bool foo=true>
class Bar {
  template <>
  typename std::enable_if<foo>::type
  push(const T& value) { /* one implementation */}

  template <>
  typename std::enable_if<!foo>::type
  push(const T& value) { /* another implementation */ } 
}

however, I am getting an error of "cannot specialize a function push within class scope" under gcc, and I do not understand why. Although my code is not exactly like that in the linked answer, it seems very similar and I can't spot the critical difference.

I also tried using a syntax similar to that suggested in this answer but it is also not working (the error is "class member cannot be redeclared" instead):

  template <bool enable=foo>
  typename std::enable_if<enable>::type
  push(const T& value) { /* one implementation */}

  template <bool enable=!foo>
  typename std::enable_if<enable>::type
  push(const T& value) { /* another implementation */ } 

How can I accomplish this?

Upvotes: 4

Views: 2191

Answers (2)

Jarod42
Jarod42

Reputation: 217880

As alternatives:

  • if constexpr in C++17:

    template<class T, bool foo=true>
    class Bar {
    public:
        void push(const T& value) {
            if constexpr(foo) {
                /* one implementation */
            } else {
                /* another implementation */
            }
        }
    };
    
  • Tag dispatching:

    template<class T, bool foo=true>
    class Bar {
        void push_impl(const T& value, std::true_type) {
            /* one implementation */
        } 
        void push_impl(const T& value, std::false_type) {
            /* another implementation */
        }
    
    public:
        void push(const T& value) {
            push_impl(value, std::integral_constant<bool, foo>{});
        }
    };
    

Upvotes: 3

songyuanyao
songyuanyao

Reputation: 172964

Firstly, SFINAE works with function templates overloading; so you should go with the 2nd approach. But you declare two overloads with same signature; note that the default argument of template parameter doesn't belong to the signature.

Change it to

template <bool enable=foo>
typename std::enable_if<enable>::type
//                      ~~~~~~
push(const T& value) { /* one implementation */}

template <bool enable=foo>
typename std::enable_if<!enable>::type
//                      ~~~~~~~
push(const T& value) { /* another implementation */ } 

Upvotes: 7

Related Questions