toteload
toteload

Reputation: 169

C++ template parameter inference

As an exercise I am trying to write an array implementation using templates, but with a function pointer as a template parameter. This function would be called every time the array gets indexed.

template<typename T, int size>
using Array_fnIndex = void (*)(int index);

template<typename T, int size, typename Array_fnIndex<T, size> fnIndex>
struct Array {
    T data[size];
    T& operator[](int index) {
        fnIndex(index);
        return data[index];
    }
};

// example index function
template<typename T, int size>
void checkIndex(int index) {
    assert(index < size);
}

int main() {
    Array<int, 10, checkIndex<int, 10>> a; // this works
    Array<int, 10, checkIndex<int, 11>> b; // this also works, not what I want
    Array<int, 10, checkIndex> c; // this doesn't work, but what I want
    return 0;
}

The last Array declaration in the main function is what I would like, where the template parameters of checkIndex match the previous template parameters in Array. However this doesn't compile (using Microsoft compiler). I get the following error:

error C2440: 'specialization': cannot convert from 'void (__cdecl *)(uint)' to 'void (__cdecl *)(uint)'
note: None of the functions with this name in scope match the target type

Is there any way to get the desired result where the template parameters for the provided function get inferred from the other parameters?

Upvotes: 7

Views: 417

Answers (1)

Vittorio Romeo
Vittorio Romeo

Reputation: 93364

May not be applicable in your real use case, but I suggest a callable object containing a function that does the check:

template<typename T, int size, typename fnIndex>
struct Array {
    T data[size];
    T& operator[](int index) {
        fnIndex{}.template check<size>(index);
        return data[index];
    }
};

struct checkIndex {
    template<int size>
    void check(int index) {
        assert(index < size);
    }    
};

int main() {    
    Array<int, 10, checkIndex> c;
    return 0;
}

wandbox example


Let's analyze fnIndex{}.template check<size>(index):

fnIndex{} // make a temporary object of type `fnIndex`
    .template check<size>(index) // call its `check` method using `size` 
                                 // as a template argument and `index` as
                                 // as a function argument

The .template disambiguation syntax is required because the compiler does not know what check means - it could be a field and the line could be interpreted as:

fnIndex{}.check < size > (index)

where < is the less-than operator, > is the greater-than operator, and (index) is an expression.

Using .template tells the compiler that we want to invoke a template method.

Upvotes: 6

Related Questions