Jared Hoberock
Jared Hoberock

Reputation: 11416

How to check for static member variable template?

I need to define a class, foo, with a static member variable template, foo::static_variable_template<T>. This member should only exist when T fulfills certain requirements. For example, when the constexpr static function T::constexpr_static_function() exists. Otherwise, foo::static_variable_template<T> should not exist. Moreover, I would like to be able to test for the existence of foo::static_variable_template<T> at compile-time via SFINAE.

Here is an approximation of what I would like to do:

#include <iostream>

struct foo
{
  template<class T>
    static constexpr int static_variable_template =
      T::constexpr_static_function();

// XXX this works but requires a second defaulted template parameter
//  template<class T, int = T::constexpr_static_function()>
//    static constexpr int static_variable_template =
//      T::constexpr_static_function();
};

struct has_constexpr_static_function
{
  static constexpr int constexpr_static_function() { return 42; }
};

struct hasnt_constexpr_static_function
{
};

template<class T, class U,
  int = T::template static_variable_template<U>>
void test_for_static_variable_template(int)
{
  std::cout << "yes it has\n";
}

template<class T, class U>
void test_for_static_variable_template(...)
{
  std::cout << "no it hasn't\n";
}

int main()
{
  test_for_static_variable_template<foo, has_constexpr_static_function>(0);
  test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}

This approximation nearly works, but only if foo::static_variable_template has a second, defaulted template parameter. Because this second parameter is an implementation detail, I'd like to hide it from the public interface of foo::static_variable_template.

Is this possible in C++17?

Upvotes: 2

Views: 1206

Answers (1)

cantordust
cantordust

Reputation: 1612

I am not sure if your intent is to initialise foo::static_variable_template with 0 if T::constexpr_static_function() is missing or you want to disable it entirely. In case of the former, this might be useful. For example, this (clunky) solution works (requires C++17 for if constexpr; note that your variable is now a function):

#include <iostream>

template <typename T>
class has_func
{
    typedef char does;
    typedef long doesnt;

    template <typename C> static does test( decltype(&C::constexpr_static_function) );
    template <typename C> static doesnt test(...);

public:
    static constexpr bool value()
    {
        return sizeof(test<T>(0)) == sizeof(char);
    }
};

struct foo
{
    template<class T>
    static constexpr int static_variable_template()
    {
        if constexpr (has_func<T>::value())
        {
            return T::constexpr_static_function();
        }
        return 0;
    }

    // XXX this works but requires a second defaulted template parameter
    //  template<class T, int = T::constexpr_static_function()>
    //    static constexpr int static_variable_template =
    //      T::constexpr_static_function();
};

struct has_constexpr_static_function
{
    static constexpr int constexpr_static_function() { return 42; }
};

struct hasnt_constexpr_static_function
{
};

template<class T, class U>
void test_for_static_variable_template(...)
{
    if constexpr (has_func<U>::value())
    {
        std::cout << "yes it has\n";
    }
    else
    {
        std::cout << "no it hasn't\n";
    }
}

int main()
{
    std::cout << foo::static_variable_template<has_constexpr_static_function>() << "\n";
    std::cout << foo::static_variable_template<hasnt_constexpr_static_function>() << "\n";

    /// Original test
    test_for_static_variable_template<foo, has_constexpr_static_function>(0);
    test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}

Prints

42
0
yes it has
no it hasn't

Tested with clang 5.0.1.

In case you want to disable foo::static_variable_template entirely, you might need to use std::enable_if:

#include <iostream>

template <typename T>
class has_func
{
    typedef char does;
    typedef long doesnt;

    template <typename C> static does test( decltype(&C::constexpr_static_function) );
    template <typename C> static doesnt test(...);

public:
    static constexpr bool value()
    {
        return sizeof(test<T>(0)) == sizeof(char);
    }
};

struct foo
{
    template<class T, typename std::enable_if<has_func<T>::value()>::type ...>
    static constexpr int static_variable_template()
    {
        if constexpr (has_func<T>::value())
        {
            return T::constexpr_static_function();
        }
        return 0;
    }

    // XXX this works but requires a second defaulted template parameter
    //  template<class T, int = T::constexpr_static_function()>
    //    static constexpr int static_variable_template =
    //      T::constexpr_static_function();
};

struct has_constexpr_static_function
{
    static constexpr int constexpr_static_function() { return 42; }
};

struct hasnt_constexpr_static_function
{
};

template<class T, class U>
void test_for_static_variable_template(...)
{
    if constexpr (has_func<U>::value())
    {
        std::cout << "yes it has\n";
    }
    else
    {
        std::cout << "no it hasn't\n";
    }
}

int main()
{

    std::cout << foo::static_variable_template<has_constexpr_static_function>() << "\n";
    // We can't print this because it doesn't exist.
    // std::cout << foo::static_variable_template<hasnt_constexpr_static_function>() << "\n";

    /// Original test
    test_for_static_variable_template<foo, has_constexpr_static_function>(0);
    test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}

In this line of thought, I am not sure if you can disable a static template variable with std::enable_if. To quote the great Riemann, "I have for the time being, after some fleeting vain attempts, provisionally put aside the search for this..."

Upvotes: 1

Related Questions