5

I am trying to use some SFINAE inside a templated struct. I reduced my problem to the following and could make this work:

template<bool mybool>
struct test {
    void myfunc();
};

template<bool mybool>
void test<mybool>::myfunc() {
    std::cout << "test true" << std::endl;
}
template<>
void test<false>::myfunc() {
    std::cout << "test false" << std::endl;
}

int main(int argc, char ** argv) {
    test<true> foo;
    test<false> bar;
    foo.myfunc();
    bar.myfunc();
}

With this code, I get the result:

test true
test false

However, if I want to consider that my struct test with more than one template parameter, I tried adapting the above like this:

template<int myint, bool mybool>
struct test {
    void myfunc();
};

template<int myint, bool mybool>
void test<myint,mybool>::myfunc() {
    std::cout << "test true" << std::endl;
}
template<int myint>
void test<myint,false>::myfunc() { 
//error: invalid use of incomplete type 'struct test<myint, false>'
    std::cout << "test false" << std::endl;
}

int main(int argc, char ** argv) {
    test<1,true> foo;
    test<1,false> bar;
    foo.myfunc();
    bar.myfunc();
}

I am getting an invalid use of incomplete type 'struct test'.

Am I going in the wrong direction? Is there a way to do what I want to do? Thanks for your help!

Kiplaki
  • 171
  • 6
  • Did you mean `foo.myfunc()` when you wrote `foo.test()`? – bitmask May 24 '13 at 09:02
  • You also spelled `myfunc` wrong in the second example. It should be `my_func`. Please try your examples before posting them. – pmr May 24 '13 at 09:45

4 Answers4

6

You cannot partially specialize member function, you should partially specialize full struct. Following example will work correctly

template<int myint, bool mybool>
struct test {
    void my_func();
};

template<int myint, bool mybool>
void test<myint,mybool>::my_func() {
    std::cout << "test true" << std::endl;
}

template<int myint>
struct test<myint, false> {
   void my_func();
};

template<int myint>
void test<myint,false>::my_func() { 
//error: invalid use of incomplete type 'struct test<myint, false>'
    std::cout << "test false" << std::endl;
}

int main(int argc, char ** argv) {
    test<1,true> foo;
    test<1,false> bar;
    foo.my_func();
    bar.my_func();
}
ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • Ok, tried your solution and it indeeed work. However, let's say I have a lot of attribute in my class, I really don't want to declare them in both the structures. But if I declare them only in the first one, I won't be able to access them from the specialized one. Is there a way for me not to double the declaration of everything? – Kiplaki May 24 '13 at 09:04
  • 1
    @Kiplaki yes, use some one base / traits type. – ForEveR May 24 '13 at 09:07
  • @Kiplaki Or dispatch on an overload with `false_type/true_type`. – pmr May 24 '13 at 09:46
3

If you want to avoid redefining your class, which you would have to do since partial specialisation of (member) functions is not allowed, you could decompose your type. This will minimise the repetition of code:

template<int myint, bool mybool>
struct test {
    char some_var;
    std::vector<int> more_var;
    void my_func();
};

Change to:

template<int myint>
struct test_static {
  protected:
    char some_var;
    std::vector<int> more_var;
};

template <int myint, bool mybool>
struct test : private test_static<myint> {
    void my_func() {
      // default case
    }
};
template <int myint>
struct test<myint,false> : private test_static<myint> {
    void my_func() {
      // special case
    }
};

Of course, if you want full visibility of all members to the outside, don't make them protected in the first place and use public instead of private inheritance.

bitmask
  • 32,434
  • 14
  • 99
  • 159
3

Looking first at this question on the SFINAE principle to refresh my memory, I tried to get the result you are looking for with minimal redundancy in the code.

I also checked the wikipedia article on the subject, which indicated me that you need a functionality similar too boost::enable_if to conditionally choose your function inmplementation:

// simplified version of boost::enable_if_c and disable_if_c to match your exact need

template <bool B>
struct enable_if_c {
    typedef void type;
};

struct enable_if_c<false>{};

template <bool B>
struct disable_if_c {
    typename void type;
};

struct disable_if_c<true> {};

template<bool mybool, typename T>
struct test {
    template <bool d> 
    typename enable_if_c<d>::type my_func_impl(){
        cout << "true" << endl;
    }
    template <bool d>
    typename disable_if_c<d>::type my_func_impl(){
         cout << "false" << endl;
    }
    void my_func(){ my_func_impl<mybool>(); }
};

You can define the my_func_impl bodies outside the struct with the following syntax:

 template <bool mybool, typename T>
 template <bool d>
 typename enable_if_c<d>::type test<mybool,T>::my_func_impl(){
     cout << "true" << endl;
 }

The tricky point of the problem is that you cannot rely on a simple overloading, since you want the same function prototype, hence the need to exclusively define one or the other implementation.

Community
  • 1
  • 1
didierc
  • 14,572
  • 3
  • 32
  • 52
0

You can add a little improvement to the answer provided by diderc, by just a little modification that enables you to avoid the use of an auxiliary function which would pollute your functions names :

Instead of :

template <bool d> 
typename enable_if_c<d>::type my_func_impl(){
    cout << "true" << endl;
}
template <bool d>
typename disable_if_c<d>::type my_func_impl(){
     cout << "false" << endl;
}
void my_func(){ my_func_impl<mybool>(); }

Just write :

template <bool d = mybool> 
typename enable_if_c<d>::type my_func(){
    cout << "true" << endl;
}
template <bool d = mybool>
typename disable_if_c<d>::type my_func(){
     cout << "false" << endl;
}

And if you can use C++11, then you can replace enable_if_c and disable_if_c by std::enable_if.

( I can't comment his answer, so I posted my own )

Community
  • 1
  • 1
Nemikolh
  • 700
  • 4
  • 19