Home > Net >  C Template - passing const value of type T by reference
C Template - passing const value of type T by reference

Time:07-01

I have a function with a template parameter T and would like to pass a value of type const T by reference. The C compiler throws an error, (kind of) understandably so. Hence I was wondering if there exists a way to do this in a safe and concise way?

I created a very small example that reflects the issue I am having in my project. (in my project the issue appears in a constant member function of some class, but from my experiments the issue should be "faithfully" reflected in the example below by use of a constant variable of int instead for simplicity's sake).

I am aware that I could theoretically use a separate template parameter "cT", but that would be horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument ... I also understand that I could simply refrain from using templates at all and just specify this for every type. I was just wondering if what I am trying to achieve below can be done with templates.

Thanks and have a nice day! :)

template<typename T>
bool ContainsElement(std::list<T>& findList, const T& elem)
{
    for (auto& entry : findList)
    {
        if (entry == elem)
            return true;
    }
    return false;
}

int main()
{
    std::list<int*> myList;
    const int testConst = 6;
    auto pointerToTestConst = &testConst;

    ContainsElement(myList, pointerToTestConst); // compiler screams
}

CodePudding user response:

The issue is in incompatibility between pointers:

  • pointerToTestConst is of type const int* - non-const pointer to const integer. Therefore T=const int*
  • myList is of type list<int*>, deducing T=int*.

Since those types are not the same, compilation fails. Rightfully so, because elem would allow changing testConst if T=int*.

The issue manifests regardles you passing elem by (const) reference or value.

horribly unsafe, as the caller of this function need not pass an object of "const T" as second argument

So what? They get a compiler error about the comparison. It is no more unsafe than your code right now. I would argue that a generic ContainsElement should not care what you pass to is as long as it compares equal to some element in the list, it is a match.

Of course STL already offers this for std::find and std::ranges::find which also does not care and it is not called unsafe because of it.

CodePudding user response:

To "normalize" the type for pointers and non-pointers, std::conditional may be used, finally your template still defines single T parameter:

#include <type_traits>

template<typename T>
bool ContainsElement(std::list<T>& findList,
const std::conditional_t<
        std::is_pointer<T>::value,
        std::add_pointer_t<
                std::add_const_t<
                        std::remove_pointer_t<T>
                >
        >,
        std::add_const_t<T> 
>& elem)
{
    for (auto& entry : findList)
    {
        if (entry == elem)
            return true;
    }
    return false;
}

Above example uses helper types with postfix _t introduced in C 14, for C 11, use std::conditional::type.

It works for std::list<int> and std::list<int*>.

Of course in case of std::list<int*> it will check whether list contains exactly the same pointer address, not the pointed value.

  • Related