Search code examples
c++constexprenable-if

How to do a constexpr count based on a type trait


I have a database of objects and would like to count how many are of a particular type at compile time, but I'm having a bit of trouble getting this to compile.

Here's a cut down example of what I've been trying so far, but this fails to compile with "error: call to function 'do_count' that is neither visible in the template definition nor found by argument-dependent lookup"

Is there a better way?

#include <cstdint>
#include <type_traits>
#include <cstddef>

struct unused_tag {};
struct used_tag {};

template<std::size_t X>
struct traits {
    using type = unused_tag;
};

template<>
struct traits<7> {
    using type = used_tag;
};

static constexpr const std::size_t MAX_X = 10;

template<std::size_t X = 0>
constexpr
std::enable_if_t<
    !std::is_same<typename traits<X>::type, unused_tag>::value,
    std::size_t>
do_count()
{
    return do_count<X + 1>() + 1;
}

template<std::size_t X = 0>
constexpr
std::enable_if_t<
    std::is_same<typename traits<X>::type, unused_tag>::value,
    std::size_t>
do_count()
{
    return do_count<X + 1>();
}

template<>
constexpr std::size_t do_count<MAX_X>()
{
    return 0;
}

static constexpr const std::size_t COUNT = do_count();

Solution

  • It seems like you found a different solution to your problem but here is a solution using std::enable_if if you were curious.

    The problem is that the call of do_count from the !std::is_same<...> version cannot see the std::is_same<...> version so, as your compiler said, it is not visible from the call site and can't be resolved. To fix this, just make a forward declaration of the std::is_same<...>.

    For your example, the following compiles for me:

    #include <cstdint>
    #include <type_traits>
    #include <cstddef>
    
    struct unused_tag {};
    struct used_tag {};
    
    template<std::size_t X>
    struct traits {
        using type = unused_tag;
    };
    
    template<>
    struct traits<9> {
        using type = used_tag;
    };
    
    static constexpr const std::size_t MAX_X = 10;
    
    //
    // forward declaration
    //
    template<std::size_t X = 0>
    constexpr
    std::enable_if_t<
        std::is_same<typename traits<X>::type, unused_tag>::value,
        std::size_t>
    do_count();
    
    template<std::size_t X = 0>
    constexpr
    std::enable_if_t<
        !std::is_same<typename traits<X>::type, unused_tag>::value,
        std::size_t>
    do_count()
    {
        return do_count<X + 1>() + 1;
    }
    
    template<std::size_t X>
    constexpr
    std::enable_if_t<
        std::is_same<typename traits<X>::type, unused_tag>::value,
        std::size_t>
    do_count()
    {
        return do_count<X + 1>();
    }
    
    template<>
    constexpr std::size_t do_count<MAX_X>()
    {
        return 0;
    }
    
    static constexpr const std::size_t COUNT = do_count();