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.
Related
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.
In an attempt to call a function template which accepts a type and a parameter/argument of that type as the template parameters/arguments, compiler gives an error which is not produced with similar parameters/arguments. So I was wondering what is the correct parameters/arguments in case of calling the function templates for the member function "operator[]const" of a vector class!
Consider this piece of code:
class test_class{
public:
int member;
int& operator[](size_t) {return member;}
const int& operator[](size_t) const{return member;}
};
typedef std::vector<int> vector_type;
typedef const int&(test_class::* OK_type)(size_t)const;
typedef const int&(vector_type::* not_OK_type)(size_t)const;
static constexpr OK_type OK_pointer = &test_class::operator[];
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
template<typename t__, t__>
void function(){}
The above code is alright now consider the main function:
int main() {
function<OK_type, OK_pointer>();
function<not_OK_type, not_OK_pointer>();
return 0;
}
The first call of the function template is OK but not the second one.
The error which compiler produce is:
error: no matching function for call to ‘function<not_OK_type, not_OK_pointer>()’
note: candidate: ‘template<class t__, t__ <anonymous> > void function()’
note: template argument deduction/substitution failed:
error: ‘const int& (std::vector<int>::*)(size_t) const{((const int& (std::vector<int>::*)(size_t) const)std::vector<int>::operator[]), 0}’ is not a valid template argument for type ‘const int& (std::vector<int>::*)(long unsigned int) const’
function<not_OK_type, not_OK_pointer>();
note: it must be a pointer-to-member of the form ‘&X::Y’
Interestingly even if the function template was formed as:
template<auto>
void function(){}
it would cause the same result.
I must add that in the case of non const version, error is the same (for std::vector).
So I am wondering
A: what is wrong?
B: Considering that if there was a mismatch between the not_OK_type and &vector_type::operator[], then compiler would also give an error in case of:
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
Is there a difference between types that can be used as constexpr and the type that can be used as a template parameter/argument?
The issue is typedef const int&(vector_type::* not_OK_type)(size_t)const;.
If you see stl_vector.h (here), the operator[] is declared as noexcept at line 1040.
But in the declaration of not_OK_type variable, noexcept is not present. That's why the compiler complains.
For getting rid of compilation error, add noexcept to not_OK_type variable. Like this:
typedef const int&(vector_type::* not_OK_type)(size_t)const noexcept;
Working code:
#include <vector>
class test_class{
public:
int member;
int& operator[](size_t) {return member;}
const int& operator[](size_t) const{return member;}
};
typedef std::vector<int> vector_type;
typedef const int&(test_class::* OK_type)(size_t)const;
typedef const int&(vector_type::* not_OK_type)(size_t)const noexcept;
static constexpr OK_type OK_pointer = &test_class::operator[];
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
template<typename t__, t__>
void function(){}
int main() {
function<OK_type, OK_pointer>();
function<not_OK_type, not_OK_pointer>();
return 0;
}
#Kunal Puri, provided a correct answer for the the section A of the question.
As for the section B of the question, I guess the clue can be found in one exception that applies when converted constant expression used as template argument for a non-type template parameter.
According to (https://en.cppreference.com/w/cpp/language/constant_expression), a converted constant expression under certain condition and specifically in case of conversion of pointer to noexcept function to pointer to function is a constant expression. Which explains why compiler did not produce an error in case of:
static constexpr not_OK_type not_OK_pointer = &vector_type::operator[];
However according to (https://en.cppreference.com/w/cpp/language/template_parameters), this type of converted constant expression can not be used as a pointer to non-static data members(and maybe also a pointer to non-static member functions) for a non-type template parameter.
This exception might be the source of conflict between types that can be used as constexpr and as template argument, although statements in the later source are vague and not directly linked to the case.
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;
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&&);
// ^^^
Given GMan's deliciously evil auto_cast utility function concocted here, I've been trying to figure out why it doesn't compile for me when I'm trying to auto_cast from an rvalue (on MSVC 10.0).
Here's the code that I'm using:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(pX) { }
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
int main()
{
int c = auto_cast( 5.0f ); // from an rvalue
}
To the best of my ability I've tried to follow the C++0x reference collapsing rules and the template argument deduction rules outlined here, and as far as I can tell the code given above should work.
Recall that in pre-0x C++, it is not allowed to take a reference to a reference: something like A& & causes a compile error. C++0x, by contrast, introduces the following reference collapsing rules:
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
The second rule is a special template argument deduction rule for function templates that take an argument by rvalue reference to a template argument:
template<typename T>
void foo(T&&);
Here, the following rules apply:
When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&.
When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&.
Now when I mouse over the call to auto_cast( 5.0f ), the tooltip correctly displays its return value as auto_cast_wrapper<float>. This meaning that the compiler has correctly followed rule 2:
When foo is called on an rvalue of type A, then T resolves to A.
So since we have an auto_cast_wrapper<float>, the constructor should instantiate to take a float&&. But the error message seems to imply that it instantiates to take a float by value.
Here's the full error message, showing again that T=float correctly yet the T&& parameter becomes T?
main.cpp(17): error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
You cannot bind an lvalue to an rvalue reference
main.cpp(17) : while compiling class template member function 'auto_cast_wrapper<T>::auto_cast_wrapper(T &&)'
with
[
T=float
]
main.cpp(33) : see reference to class template instantiation 'auto_cast_wrapper<T>' being compiled
with
[
T=float
]
Any thoughts?
You forgot to std::forward the T&& argument to the auto_cast_wrapper constructor. This breaks the forwarding chain. The compiler now gives a warning but it seems to work fine.
template <typename T>
class auto_cast_wrapper
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }
auto_cast_wrapper(const auto_cast_wrapper&);
auto_cast_wrapper& operator=(const auto_cast_wrapper&);
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
float func() {
return 5.0f;
}
int main()
{
int c = auto_cast( func() ); // from an rvalue
int cvar = auto_cast( 5.0f );
std::cout << c << "\n" << cvar << "\n";
std::cin.get();
}
Prints a pair of fives.
Sorry for posting untested code. :)
DeadMG is correct that the argument should be forwarded as well. I believe the warning is false and the MSVC has a bug. Consider from the call:
auto_cast(T()); // where T is some type
T() will live to the end of the full expression, which means the auto_cast function, the auto_cast_wrapper's constructor, and the user-defined conversion are all referencing a still valid object.
(Since the wrapper can't do anything but convert or destruct, it cannot outlive the value that was passed into auto_cast.)
I fix might be to make the member just a T. You'll be making a copy/move instead of casting the original object directly, though. But maybe with compiler optimization it goes away.
And no, the forwarding is not superfluous. It maintains the value category of what we're automatically converting:
struct foo
{
foo(int&) { /* lvalue */ }
foo(int&&) { /* rvalue */ }
};
int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue
And if I'm not mistaken the conversion operator shouldn't be (certainly doesn't need to be) marked const.
The reason it doesn't compile is the same reason for why this doesn't compile:
float rvalue() { return 5.0f }
float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
As a is itself an lvalue it cannot be bound to b. In the auto_cast_wrapper constructor we should have used std::forward<T> on the argument again to fix this. Note that we can just use std::move(a) in the specific example above, but this wouldn't cover generic code that should work with lvalues too. So the auto_cast_wrapper constructor now becomes:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
...
private:
auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }
T&& mX;
};
Unfortunately it seems that this now exhibits undefined behaviour. I get the following warning:
warning C4413: 'auto_cast_wrapper::mX' : reference member is initialized to a temporary that doesn't persist after the constructor exits
It appears that the literal goes out of scope before the conversion operator can be fired. Though this might be just a compiler bug with MSVC 10.0. From GMan's answer, the lifetime of a temporary should live until the end of the full expression.