Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template parameter inference

Tags:

c++

templates

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?

like image 970
toteload Avatar asked Jan 09 '17 12:01

toteload


1 Answers

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.

like image 190
Vittorio Romeo Avatar answered Oct 17 '22 06:10

Vittorio Romeo