Constant References with Typedef and Templates in C++

const references in c++ templates

Remove the reference:

template<typename T>
void Test(const typename std::remove_reference<T>::type & param)
{
param = 20;
}

Now it works as expected.

C++: template function with explicitly specified reference type as type parameter

§8.3.2 [dcl.ref]/p6:

If a typedef-name (7.1.3, 14.1)* or a decltype-specifier (7.1.6.2)
denotes a type TR that is a reference to a type T, an attempt to
create the type “lvalue reference to cv TR” creates the type “lvalue
reference to T”, while an attempt to create the type “rvalue reference
to cv TR” creates the type TR.

This is known as reference collapsing and is introduced in C++11. In C++03, your code would be ill-formed, but some compilers supported it as an extension.

Note that in const T &, the const applies to the type T, so when T is int &, the const would apply to the reference type itself (which is meaningless as references are immutable anyway), not the type referred to. This is why the reference collapsing specification ignores any cv-qualifiers on TR.


*A template type parameter is a typedef-name, per §14.1 [temp.param]/p3:

A type-parameter whose identifier does not follow an ellipsis
defines its identifier to be a typedef-name (if declared with
class or typename) or template-name (if declared with
template) in the scope of the template declaration.

How can const be applied to template argument types outside of the parameter list in C++?

Template type substitutions are not textual, so do not think of them in terms of the textual type definition.

In your example, T is deduced to be int * - let's call it intptr. You are making const reference to it, so return value becomes const intptr&. That means, that the pointer itself can't be modified through this reference, but the value it points to can be modified.

Last, but not the least, you could have easily verifed your assumptions before asking the question :)

How to use a const reference as template parameter?

I won't directly answer your question, but I dare to guess what you really need to hear :)

First, having references as types of templates is not usually a good idea. If you want to make a wrapper, it makes sense to have a reference on a wrapped inside your class, but it is still better to keep template parameter type to be a value type, not a reference type. Having said that, if you are really sure you want to have reference type as a parameter, you might want to play with std::decay.

Then, I see you have partial template specialization here. From the code you posted it is not clear if you actually need it or not. I personally love to keep things simple, so I'd suggest you're good without it. In this case just parametrize your class on the one and only type T.

As a side note, do not declare your functions inline. Compiler knows best for you. And methods defined in scope of a class are inline by default anyway. Forget that inline means "please make this code faster". This actually means "this symbol might appear in several translation units, please pick a single definition for me".

Finally, boost is a great library, but I don't see how you need it here. All standard containers provide all the necessary type aliases in them, just ask.

Here is the compiling code simplified according to my comments:

#include <vector>

template <class T>
class matrix_wrapper
{
public:
using reference = const T&;
using vector_type_ref = typename T::reference;
using vector_type_const_ref = typename T::const_reference;

matrix_wrapper(reference data) : m_data(data) {}

vector_type_const_ref operator[](size_t i) const
{
return m_data[i];
}
// BTW this won't compile for non-const objects, since you store a const
// reference to the container, but that's a different story
vector_type_ref operator[](size_t i)
{
return m_data[i];
}
reference data()
{
return m_data;
}

protected:
reference m_data;
};

void test(const std::vector<std::vector<int>>& data)
{
matrix_wrapper<std::vector<std::vector<int>>> matrix(data);
}

int main()
{
std::vector<std::vector<int>> v(10, std::vector<int>(10, 1));
test(v);
}

See live demo here: https://wandbox.org/permlink/tmemloS6wCHZlhNY

I see that you declared v in main() as a vector of ints, but you accept a vector of double in test(). I assumed this is a misprint and fixed the types.

I completely agree with @NathanOliver about 1D/2D thing, but that's again a different story.

How can I declare a template constant type?

C++11:

template <typename T>
using Constant = const T;

Constant<int> i = 1;
//! i = 2; // error: assignment of read-only variable 'i'

C++03:

template <typename T>
struct Constant
{
typedef const T type;
};

Constant<int>::type i = 1;

A good way to organize a code involving const references with limited scope and templates in C++

Invert control flow and a runtime to compile time dispatcher.

This is insanely complex in c++11, and modestly complex in c++14.

In c++14 you can get:

pick( some_boolean_statement,
&getRef<TYPE_A>,
&getRef<TYPE_B>
)([&](auto* getRef){
const auto& ref = getRef();
doStuff(ref);
});

but basically every step along that way is a pain in c++11.

Another approach would be to make a std (c++17) or boost (c++03) variant that stores a pointer to TYPE_A or TYPE_B. Then use visit; but even here, you'd need an auto lambda to keep your code short.

The simplest c++14 version is:

auto doWork = [&](const auto& ref) {
doStuff(ref);
};
if (some_boolean_statement) {
doWork(getRef<TYPE_A>());
} else {
doWork(getRef<TYPE_B>());
}


Related Topics



Leave a reply



Submit