I have the following code:
template <typename T>
struct Val
{
T val;
const T& get() const { return val; }
void set(const T& newVal) { val = newVal; }
};
int& f(const Val<int>& val)
{
return val.get();
}
int* g(const Val<int*>& val)
{
return val.get();
}
int main()
{
Val<int> val1;
f(val1);
Val<int*> val2;
g(val2);
return 0;
}
It fails compilation in gcc with the following message:
main.cpp: In function 'int& f(const Val<int>&)':
main.cpp:78:20: error: invalid initialization of reference of type 'int&' from expression of type 'const int'
return val.get();
^
main.cpp:79:1: warning: control reaches end of non-void function [-Wreturn-type]
That's totally fine, but why isn't the same error produced for g()? Why, for some reason, is the const qualifier removed on const T&, when T is a pointer?
I tried to find some resources, but it seems hard to find. I know Meyers wrote something in his newest book, but I don't have access to that one. Can someone point me to resources or keywords where I could find more on that whole topic of template type deduction when T is a pointer?
Why, for some reason, is the const qualifier removed on const T&, when T is a pointer?
Because you're returning the pointer by value, not by reference. It is perfectly fine to initialize a pointer from a const pointer (note it is the pointers that are const, not the things they point to.) You're doing the equivalent of this:
int* const p0 = nullptr; // const pointer
int* p1 = p0; // copy to non-const is OK
If you were to initialize a non-cost reference to poiter from a const pointer, you'd get a similar error to the first case:
// error: binding of reference to type 'int *'
// to a value of type 'int *const' drops qualifiers
int*& p1 = p;
Related
I've reduced my code down to the following minimum code:
#include<iostream>
#include<vector>
class tt
{
public:
bool player;
std::vector<tt> actions;
};
template<typename state_t>
int func(state_t &state, const bool is_max)
{
state.player = true;
const auto &actions = state.actions;
if(state.actions.size())
{
auto soln = func(actions[0], false);
}
return 0;
}
int main(int argc, char const *argv[])
{
tt root;
func(root, true);
return 0;
}
When I try to compile this code, I get
test.cpp:14:17: error: cannot assign to variable 'state' with const-qualified type 'const tt &'
state.player = true;
~~~~~~~~~~~~ ^
test.cpp:19:19: note: in instantiation of function template specialization 'func<const tt>' requested here
auto soln = func(actions[0], false);
^
test.cpp:28:4: note: in instantiation of function template specialization 'func<tt>' requested here
func(root, true);
^
test.cpp:12:19: note: variable 'state' declared const here
int func(state_t &state, const bool is_max)
~~~~~~~~~^~~~~
1 error generated.
It is claiming that state is a const tt & type. The signature of the templated function is int func(state_t &state, const bool is_max), and there is no const in front of the state_t. It appears the const is somehow being deduced from the recursive call because actions is a const-ref vector of tt objects. I thought argument deduction ignores const? How can this occur?
Answer is mainly extracted from Scott Mayers Effective C++ book.
template<typename T>
void f(ParamType param);
f(expr); // deduce T and ParamType from expr
ParamType is a Reference or Pointer, but not a Universal Reference
The simplest situation is when ParamType is a reference type or a pointer type, but not a universal reference. In that case, type deduction works like this:
If expr’s type is a reference, ignore the reference part.
Then pattern-match expr’s type against ParamType to determine T.
In argument deduction process it ignores the reference part not the const part.
In your case it is const auto &actions = state.actions; which means, for the template argument deduction of auto soln = func(actions[0], false); only the reference part is dropped, not the cv qualifiers.
Further examples from the book.
template<typename T>
void f(T& param); // param is a reference
and we have these variable declarations,
int x = 27; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
the deduced types for param and T in various calls are as follows:
f(x); // T is int, param's type is int&
f(cx); // T is const int,
// param's type is const int&
f(rx); // T is const int,
// param's type is const int&
In addition to #aep's answer, If it had been deduced as tt instead of const tt, the compiler would generated an error too, because it is not possible to bind const reference to non-const reference without const_cast.
#include<iostream>
#include<vector>
class tt
{
public:
bool player;
std::vector<tt> actions;
};
void try_to_bind_const_reference_to_non_const_reference( tt& t )
{
}
template<typename state_t>
int func(state_t &state, const bool is_max)
{
state.player = true;
const auto &actions = state.actions;
if(state.actions.size())
{
// auto soln = func(actions[0], false);
try_to_bind_const_reference_to_non_const_reference( actions[0] );
}
return 0;
}
int main(int argc, char const *argv[])
{
tt root;
func(root, true);
return 0;
}
run online
template<typename T> struct SomeClass{
void someFunc(const T& data) const {}
};
void testFunc(const int* a) {
SomeClass<int*> some_class;
some_class.someFunc( a);
}
I made a template instance with a non-const type. Now when calling a certain function I get errors that say:
error: invalid conversion from ‘const int*’ to ‘int*’
note: initializing argument 1 of ‘void SomeClass<T>::someFunc(const T&) const [with T = int*]’
So basically my const T& is treated as plain T&, the const is ignored. Why? How can I make sure in this case that it is seen by the compiler as const T&?
You may want to consider to partial specialize your class template SomeClass for the case T is a pointer. Then, add const to the type pointed to instead of the pointer itself (i.e., pointer to const instead of const pointer):
template<typename T> struct SomeClass<T*> {
void someFunc(const T* &data) const { /* ... */ }
};
SomeClass<int*>::someFunc() (i.e., T = int*) will be instantiated to:
void someFunc(const int* &data) const;
data above is a reference to a pointer to const int. However, with your primary template, SomeClass<int*>::someFunc() is actually:
void someFunc(int* const &data) const;
That is, data here is a reference to a const pointer to int. Therefore, you can't pass a, which is a const int*, as an argument to someFunc() since that pointed const int would be modifiable through the parameter data. In other words, the constness would be lost.
You need to change your definition to SomeClass<const int*> some_class;. The T comes from the definition and is int*, compiler is complaining rightfully.
The const is not ignored. It's applied to the type int*, yielding an int* that cannot be modified, i.e., int* const. In const int*, the const applies to the int, not to the pointer. That is, const int* points at an int that cannot be modified.
Inside testFunc you end up both consts. Since it's called with a const int*, the specialization of SomeClass has to be SomeClass<const int*>. And then when you call someFunc you get the second one; the actual argument type is const int* const. The first const applies to the int and the second const applies to the argument itself, i.e., to the pointer.
Assuming that the code base is huge and therefore you can't afford to write a specialization for your class template, you could provide the following delegating member template, someFunc(), which is an overload of your original member function:
#include <type_traits>
template<typename T> struct SomeClass {
// your original member function
void someFunc(const T &data) const { /* ... a lot of stuff ... */ }
// delegating member template
template<typename S>
void someFunc(const S* &data) const {
// delegate to original function
someFunc(const_cast<S*>(data));
}
};
First, this member template only comes into play with pointer arguments. Second, what it really does is to delegate the call to your original member function with the same name by casting out the const from the pointed type.
I hope it helps.
This is a very stupid example I came up while working with threads in Windows to wrap a const_cast and a static_cast into one
// Somewhere in the OS API
using function_t = void(*)(void *);
void create_thread(function_t function, void *params);
template <typename T, typename U>
T static_const_cast(U ptr) {
return const_cast<T>(static_cast<const T>(ptr));
}
void thread_proc(const void* name) {
// ...
}
void test() {
const char *name = "name";
void *params = static_const_cast<void *>(name); // Fails
// void *params = const_cast<void *>(static_cast<const void *>(name));
// Succeeds (what I want to achieve with static_const_cast)
create_thread(reinterpret_cast<function_t>(thread_proc), params);
}
but it won't compile on Visual Studio 2017, so I tried gcc and clang on Godbolt thinking it should surely be able to compile, but still refuses to with the error message (from gcc 8.2):
error: invalid static_cast from type 'const char*' to type 'void* const'
^^^^^^^^^^^^^
Why does it try to cast to void* const instead of const void* as expected? Is there something obvious I am missing?
Why does this function cast 'const char*' to 'void* const' and not 'const void*'
Because you used void * as the template argument, and when you apply const to that, it becomes void * const. void * is a pointer (to non-const object), and making a pointer const results in a const pointer, not a (non-const) pointer to const object.
You could do something like this to achieve what you wanted:
return const_cast<T>(
static_cast<std::remove_pointer_t<T> const*>(ptr)
);
However, I recommend instead to use a custom class to wrap the data that you send to the thread. Also, don't cast the function type. Use a function with void* argument, and cast the argument within the function.
struct params_t {
const char *name;
};
static params_t params {"name"};
create_thread(thread_proc, ¶ms);
No casts needed whatsoever (except obviously in the callback).
i have a class with a overladed operators.
class sout {
public:
template<typename T>
friend inline sout& operator&(sout&, T&);
friend inline sout& operator&(sout&, std::string&);
};
now if i use the templated operator& inside the the sting.operator& i get an error:
code:
sout& operator&(sout& s, std::string& str) {
uint16_t ts = static_cast<uint16_t>(str.size()); // this is ok
s & ts; // is also ok
s & static_cast<uint16_t>(str.size()); // but this is wrong
// ...
return s;
}
error:
Error:C2679: binary '&' : no operator found which takes a right-hand operand of type 'uint16_t' (or there is no acceptable conversion)
could be 'sout &operator &<uint16_t>(sout &,T &)'
with
[
T=uint16_t
]
or 'sout &operator &(sout &,std::string &)'
while trying to match the argument list '(sout, uint16_t)'
than i tried to use the explicite operator& template-type by:
operator&<uint16_t>(s, ts); // this also ig ok
but if i combine it, i again a error:
operator&<uint16_t>(s, static_cast<uint16_t>(str.size())
error:
'operator &' : cannot convert parameter 2 from 'uint16_t' to 'uint16_t &'
i also tried reinterpret_cast.
i know operator& is expecting a reference to uint16_t and the size() function is returning a size_t (int) not a reference. is it possible to do that in one line?
The problem is that the value returned by size() is a temporary, and temporaries are rvalues; however, your function accepts an lvalue reference. The following snippet clarifies the problem:
int foo() { return 42; }
void bar(int& i) { i++; } // Parameter is an lvalue reference to non-const
int main()
{
bar(foo()); // ERROR! Passing an rvalue to bar()
bar(1729); // ERROR! Passing an rvalue to bar()
int i = 42;
bar(i); // OK! Passing an lvalue to bar()
}
lvalue references cannot bind to rvalues, unless they are references to const.
template<typename T>
friend inline sout& operator&(sout&, T const&);
// ^^^^^
If your operator& is supposed to modify the right hand argument, so that the reference cannot be a reference to const, in C++11 you may use rvalue references (this will allow to bind to lvalues as well due to C++11's reference collapsing rules):
template<typename T>
friend inline sout& operator&(sout&, T&&);
// ^^^
I'm tracking down a C++ compiler error which I can't figure out. I've reduced it to this code:
#include <boost/config.hpp>
#include <boost/type_traits/remove_reference.hpp>
template<typename T> inline const T bar( T in )
{
typedef BOOST_DEDUCED_TYPENAME boost::remove_reference<T>::type nonref;
const nonref* inPtr = ∈
return *inPtr;
}
class Foo
{
};
int main()
{
Foo foo;
const Foo& ref = bar< Foo& >( foo );
}
which results in:
tt.cpp: In function ‘const T bar(T) [with T = Foo&]’:
tt.cpp:19:39: instantiated from here
tt.cpp:9:13: error: invalid initialization of reference of type ‘Foo&’ from expression of type ‘const nonref {aka const Foo}’
What's the actual issue here? Why is the const missing in the return value? I need the remove_reference since the actual code requires it.
Applying const to a reference type does nothing. You need to make the template argument const foo &, or else remove the reference and then add back both const and the reference in the function signature itself.
See also When should I use remove_reference and add_reference? particularly the second paragraph.
Using VisualStudio 2008 I get the following error
error C2440: 'return' : cannot convert from 'const nonref' to 'Foo &'
Changing bat to
template<typename T> inline const typename boost::remove_reference<T>::type& bar( T in )
{
typedef BOOST_DEDUCED_TYPENAME boost::remove_reference<T>::type nonref;
const nonref* inPtr = ∈
return *inPtr;
}
fixes this and the code compiles.
Returning const T from your function doesn't do what you are expecting. From your code I understand that you expect it to return const Foo&, which is a reference to an immutable object of type Foo.
But when T is Foo&, the expression const T means an immutable reference to an object of type Foo. Since the references are always immutable, the const part is just dropped (according to the paragraph 8.3.2 of the spec)! That is, your function returns Foo& and not const Foo& and that's what the compiler tries to tell you.