Anonymous1847
Anonymous1847

Reputation: 2598

How to force member functions of class templates to be instantiated?

I have recently found out that the member functions of class templates aren't instantiated until they're used, which is extremely annoying because it makes certain SFINAE constructs not work. I would like to know how to make sure that a member function of a class template specialization is always instantiated as soon as the class is instantiated -- but only using statements inside the class template definition, so that if the member function could not be instantiated, SFINAE kicks in and the compiler falls back on the general class template.

The code I was planning to use looks like this:

template <typename T, typename U>
class test_type {
    // general template; dummy parameter U is to allow specialization.
    static const bool value = false;
}

template <typename T>
class test_type<T, T> {
    // template specialization
    void f(T t) {
        // do all sorts of type-specific operations with t
        // (e.g., calling member functions)

        // if T is not suitable for these operations, I want
        // a substitution error to be generated so that the
        // general template is used instead
    }

    static const bool value = true;
}

template <typename T>
using satisfies = test_type<T, T>::value;

Upvotes: 0

Views: 113

Answers (1)

super
super

Reputation: 12938

The body of the member functions in a class will not be a part of SFINAE. The template gets instantiated depending on the signature, and once the class has been instantiated a faulty member function will lead to a hard error.

What you need to do is build a type_trait that you can place in the template specialization. Here's a toy example that checks for two different member functions.

#include <iostream>
#include <type_traits>

template <typename T>
using check_for_method = std::void_t<decltype(std::declval<T>().run()), decltype(std::declval<T>().go())>;

template <typename T, typename U = void>
class test_type {
public:
    static const bool value = false;
};

template <typename T>
class test_type<T, check_for_method<T>> {
public:
    void f(T t) {
        t.run();
        t.go();
    }

    static const bool value = true;
};

struct Foo {
    void run() {
        std::cout << "Running\n";
    }
};

struct Bar : Foo {
    void go() {
        std::cout << "Going\n";
    }
};

int main() {
    test_type<Foo> t1;
    test_type<int> t2;
    test_type<Bar> t3;

    std::cout << t1.value << " " << t2.value << " " << t3.value << std::endl;
    t3.f(Bar{});
}

Upvotes: 5

Related Questions