Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid use of incomplete type (SFINAE)

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!

like image 890
Kiplaki Avatar asked May 24 '13 08:05

Kiplaki


3 Answers

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();
}
like image 50
ForEveR Avatar answered Nov 15 '22 09:11

ForEveR


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.

like image 39
bitmask Avatar answered Nov 15 '22 09:11

bitmask


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.

like image 41
didierc Avatar answered Nov 15 '22 10:11

didierc