melak47
melak47

Reputation: 4850

Are members of a class template instantiated when the class is instantiated?

Supposedly members of a template class shouldn't be instantiated unless they are used. However this sample seems to instantiate the do_something member and the enable_if fails (which would be expected if we'd instantiated it - but AFAIK we did not).

Am I missing something really basic here?

#include <string>
#include <boost/utility.hpp>

struct some_policy {
    typedef boost::integral_constant<bool, false> condition;
};

struct other_policy {
    typedef boost::integral_constant<bool, true> condition;
};


template <typename policy>
class test {
   void do_something(typename boost::enable_if<typename policy::condition>::type* = 0) {}
};

int main() {
    test<other_policy> p1;
    test<some_policy>  p2;
}

coliru

Upvotes: 9

Views: 470

Answers (3)

Jarod42
Jarod42

Reputation: 217275

SFINAE happens only on template function/method (here, it is your class which is template),

You may do in C++11 (default template parameter for function/method):

template <typename policy>
class test {
   template <typename T = policy>
   void do_something(typename boost::enable_if<typename T::condition>::type* = 0) {}
};

You may alternatively use specialization, something like

template <bool> struct helper_do_something {};
template <> struct helper_do_something<true>
{
    void do_something() { /* Your implementation */ }
};

template <typename policy>
class test : helper_do_something<T::condition::value>
{
    // do_something is inherited (and it is present only when T::condition::value is true)
};

Upvotes: 4

jrok
jrok

Reputation: 55395

Mike Seymour already answered why it doesn't work, here's how to workaround it:

#include <string>
#include <boost/utility.hpp>

struct some_policy {
    typedef boost::integral_constant<bool, false> condition;
};

struct other_policy {
    typedef boost::integral_constant<bool, true> condition;
};

template <typename policy>
class test {
private:

   template<typename P>
   void do_something_impl(typename boost::enable_if<typename P::condition>::type* = 0) {}

public:
   void do_something()
   {
       do_something_impl<policy>();      
   }
};

int main() {
    test<other_policy> p1;
    test<some_policy>  p2;
}

Quick rule of thumb: If you want to do SFINAE, you need a member function template.

Upvotes: 6

Mike Seymour
Mike Seymour

Reputation: 254461

From C++11 14.7.1/1:

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions

So the function declaration is instantiated; that fails since it depends on an invalid type.

(Unfortunately, I don't have any historic versions of the standard to hand, but I imagine this rule was similar in C++98)

Upvotes: 8

Related Questions