CyberLuc
CyberLuc

Reputation: 327

C++ static_assert with class template argument

I want to do some static dispatch work, and let base class static_cast this pointer to derived class and call function of the same name to achieve polymorphism. I also want to use static_assert to make sure that derived class has indeed overloaded specific function (otherwise it will be endless loop). I tried to put static_assert at three places, but surprisingly find out that not all of them worked.

#include <iostream>

template<typename Type, Type Ptr>
struct MemberHelperClass;

#define DEFINE_HAS_MEMBER_FUNCTION(func) \
template<typename T, typename Type> \
static char MemberHelper_##func(MemberHelperClass<Type, &T::func>*); \
template<typename T, typename Type> \
static int MemberHelper_##func(...); \
template<typename T, typename Type> \
struct has_member_##func { \
    static constexpr bool value = sizeof(MemberHelper_##func<T, Type>(nullptr)) == sizeof(char); \
};

#define STATIC_ASSERT_HAS_MEMBER_FUNCTION(T_, F_, func) \
static_assert(has_member_##func<T_, F_>::value == 1, "function `"#func"` is undefined or inaccessible"); \

template<typename D>
class B
{
public:
    DEFINE_HAS_MEMBER_FUNCTION(f);

    // 1.??? why this assert always fail even if D::f is present
    // STATIC_ASSERT_HAS_MEMBER_FUNCTION(D, void(D::*)(), f);

    void f() {
        // 2.ok, assert fails only when B::f is called but D::f is not defined
        // STATIC_ASSERT_HAS_MEMBER_FUNCTION(D, void(D::*)(), f);

        static_cast<D*>(this)->f();
    }

protected:
    B() {
        // 3.ok, assert fails only when instance of D is declared but D::f is not defined
        // STATIC_ASSERT_HAS_MEMBER_FUNCTION(D, void(D::*)(), f);
    }
};

class D : public B<D>
{
public:
    // void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

template<typename T>
void g(B<T>* pB)
{
    pB->f();
}

int main()
{
    D d;
    g(&d);  // should print void D::f()
    return 0;
}

So why the first static_assert always fail? I thought it would be the best choice, as f is no need to be called. Moreover class D just needs to be declared, and no instance of D is required...

Upvotes: 0

Views: 1096

Answers (1)

T.C.
T.C.

Reputation: 137394

At the time B<D>'s definition is implicitly instantiated, D is incomplete. Therefore, a static_assert inside B's class definition will not see anything in the definition of D. Member function bodies are only implicitly instantiated when used, at which point D is already complete.

class D : public B<D> // <--- B<D> implicitly instantiated here
{
public:
    // void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};  // <--- D becomes complete here

Upvotes: 1

Related Questions