Losing qualifiers during template argument deduction - c++

Since I am using C++11, I have written my own make_unique function which takes a variadic template parameter pack and forwards it to the std::unique_ptr constructor. This works fine for simple data types. However, the objects I am trying to construct accept other type objects by const reference. On passing const references however, I am getting "Conversions loses qualifiers" i.e.
struct A {};
struct B { B(const A& ob) { ... } };
A ob;
auto ptr = make_unique<B>(ob); // error here
// Definition of make_unique below:
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>{ new T{std::forward<Args>(args)...} } ;
}
The error I am facing is
Conversions loses qualifiers, cannot convert argument 1 from 'const A' to 'A &'.
How do I resolve the error? From what I can understand, the template type deduction is not what I am expecting it to be.
Compiler: MSVC 2015, Update 3

This is most likely a compiler bug. It works fine with clang, g++, and also with current versions of MSVC 2017 as well as 2015. So I guess updating your Visual Studio should fix the issue.
working test example here

As it turns out, struct 'B' from the above code indeed changed it's constructor parameter to accept a non-const reference, but the documentation had no corresponding updates. Thank you everyone for the help!

Related

universal references and packed fields

Consider the following code:
#include <cstdint>
struct S {
uint32_t f1;
} __attribute__((packed)); // removing this attribute makes things work.
template <class T> void func(const T &x) {}
template <class T> void func(T &&x) {} // commenting out this line makes it "work"
int main() {
S s;
func(s.f1); // Error here in GCC, but not clang
}
GCC gives the following error:
<source>: In function 'int main()':
<source>:16:12: error: cannot bind packed field 's.S::f1' to 'unsigned int&'
16 | func(s.f1);
| ~~^~
It appears that GCC is choosing to not allow universal references to members of a packed struct, presumably due to alignment concerns. However, clang compiles the code just fine.
Adding to my confusion is that if I remove the (T &&x) overload, it works "just fine" if only the (const T &) overload exists. I would expect that if it can't use the universal-ref overload, then it would just fall back on const-ref version... but it doesn't.
Is clang incorrect here? Is GCC? Is it just undefined behavior and therefore they are both "right"?
The func(const T &x) is allowed because GCC will create a temporary to the packed member.
When adding a forwarding reference overload, the function call will resolve to a function that looks like func(uint32_t&). Since it's a mutable lvalue reference, no temporary can be created and the overload resolution fails, since there is no better match.
You can make it work by forcing const allowing the compiler to create a temporary again:
func(std::as_const(s).f1);

return a constructible from a variant being constructed from an alternative

Is it okay to have a struct that is constructible from a variant (say, from std::variant), and to return such structure implicitly constructed from one of the variant's alternatives?
Consider the following code:
#include <utility>
struct type1_t
{
};
struct type2_t
{
};
struct variant
{
/*template <typename T>
variant(T&& t)
{}*/
variant(type1_t){}
};
struct var_holder_t
{
var_holder_t(variant v)
: m_var(std::move(v))
{}
private:
variant m_var;
};
var_holder_t foo()
{
return type1_t{ }; // <====== offending line
}
MSVC 2017, 2019 allow that, but GCC and clang does not compile. Changing to the other variant's constructor or even using boost::variant or std::variant doesn't help.
Worth noting that changing the offending line to return {type1_t{ }}; makes it compile somehow. Currently I'm lost, though technically speaking adding two extra {} doesn't do much harm readability-wise, probably I'll stick to that at the moment, just wish for an answer.
Also making var_holder_t's constructor template (forwarding) helps, and it makes var_holder_t constructible from type1_t directly (and very likely a lot better performance-wise), but at the moment I want to keep var_holder_t completely non-template - it's supposed (thought, designed) to be casually-written simple code.
Update: what actually confused me is that Clang emits this message:
note: candidate constructor not viable: no known conversion from 'type1_t' to 'variant' for 1st argument var_holder_t(variant v)
Which is weird, because clearly there is a conversion from type1_t to variant, but looks like its just bogus diagnostics. To put it together, I think I can come up with a simple reason why the construction above does not work: it requires two implicit conversions, but the rules are that only one is considered.
The below statement works because list initialization is being performed.
return {type1_t{ }};
copy-list-initialization
return { arg1, arg2, ... } ; (8)
List initialization is performed in the following situations:
...
direct-list-initialization (both explicit and non-explicit constructors are considered)
...
8) in a return statement with braced-init-list used as the return expression and list-initialization initializes the returned object
Here var_holder_t is initialized with a type1_t object and it works as expected.

Lambda Syntax Incompatibility between MSVC and GCC

I have created a method with the following signature in a C++ header:
template<class _Ty>
class x {
public:
// ...
template<class func_Ty>
x *where(func_Ty &);
}
My code expects func_Ty to be callable (i.e. function pointer, lambda, or class that overloads operator()), takes a single parameter of type _Ty or _Ty &, and returns a bool or bool &. I call the function with this code (s is x<int> *):
s->where([](int i){ return i % 2 == 0;});
This compiles fine on MSVC, but GCC gives me an error:
no matching function for call to ‘x<int>::where(main()::__lambda0)’
If i add a * in front of the lambda, GCC compiles fine, but MSVC gives me an error:
error C2100: illegal indirection
Is this a bug in one of the compilers? Or maybe both of these solutions are non-standard? Either way, is there some way to make the same code work on both compilers?
As far as I know it's a VS extension that allows non-const references to bind to temporaries. The standard disallows this.
The lambda is a temporary and the parameter of where is a non-const reference.
So change:
x *where(func_Ty &);
to
x *where(const func_Ty &);
or
x *where(func_Ty);
This
template<class func_Ty>
x *where(func_Ty&&);
would work too as in this case it's a forwarding reference.

template function behavor between VC6 and VS2008

I have simple code about template function for Visual C++ 6.0 and Visual Studio 2008.
#include <stdio.h>
#include <vector>
template<typename T>
void function(const std::vector<T> &vec)
{
printf("vector version\n");
}
template<typename T>
void function(T val)
{
printf("value version\n");
}
int main()
{
std::vector<int> vec;
function(vec);
return 0;
}
I tried for each environment, and finally get
at VC6, function of value version, and
at VS2008, function of vector version.
I have 2 questions.
I have recognized priority of overloaded function call as following,
a) specialized function (without implicit type convert)
b) template function (without implicit type convert)
c) specialized function, with implicit type convert
d) template function, with implicit type convert
with this rule, the above results seems that
at VC6, b) is accepted (with <T> = std::vector<int>)
at VS2008, b) is ignored(?) and d) is accepted(?) (with <T> = int)
This means that VC6 is valid and VS2008 is wrong.
Is not my guess correct?
Although, I wish vector version is called for both VC6 and VS2008.
Can I do it?
Regards.
Actually VC6 is wrong; MS had limited support for the C++99 standard (which is when templates were standardized) in VC6 and had better support in VS2005 and up.
Calling function(vec) calls
template<typename T>
void function(const std::template vector<T>& vec)
with T as an int type because the template was deduced from the vector template type (the same as calling function<int>(vec)). If you called function(&vec) then the value function will be called since you're passing in a reference, which gets deduced to function<std::vector<int>>(vec).
If you want it to always call the proper function, then you'll need to be explicit, so you'd need to call it as such:
function< std::vector<int> >(vec)
Which will deduce to the vector version. Note the space between the >, this is to avoid the compiler thinking you meant the stream operator >>.
Hope that helps.

C++0x decltype fails to deduce member variable constness

Consider the following code:
template <typename T>
class B
{
};
template <typename T>
B<T> f(T& t)
{
return B<T>();
}
class A
{
class C {};
C c;
public:
A() {}
decltype(f(c)) get_c() const { return f(c); }
};
int main()
{
A a;
a.get_c();
}
When I try to compile this, I get the error:
test.cpp: In member function 'B<A::C> A::get_c() const':
test.cpp:31:46: error: conversion from 'B<const A::C>' to non-scalar type 'B<A::C>' requested
It seems that in the decltype, the compiler doesn't know that this is a const member function and therefore c is of type const C, and as a result incorrectly deduces the type of f(c) to be B<C> rather than B<const C> which is what it really is.
Am I doing something incorrectly, or is this a compiler bug? I use gcc 4.6, but 4.4 and 4.5 exhibit the same behaviour.
The compiler operates correctly according to the current C++0x WP. See this issue report, which is currently being worked on.
Possibly the final C++0x Standard won't change the meaning of your decltype application in the return type before the function name. You would need to move it to after the parameter list using -> decltype(f(c)), which hopefully will do The Right thing in final C++0x.
No, decltype is not supposed to take into account whether the function is const or not, because it can't. The same could have been written differently:
typedef decltype(f(c)) return_type;
return_type get_c() const { return f(c); }
Correction: decltype(f(c)) shouldn't even compile, because c is not static.
f needs to take an rvalue reference, not an lvalue reference.
I don't think you're allowed to use decltype on anything you wouldn't normally be able to call. I haven't been able to find anything in the standard that would allow you to access c, even within a decltype expression, outside of anywhere you could use c. Since you don't have a this pointer at the point you're trying to do your thing, I don't think you can do what you're trying to do. Doing so doesn't work in MSVC 2010 at least, and it has nothing to do with const.
I considered using declval to get one but you can't access A&&.c because A is an incomplete type at that point. I can't see anyway to do what you're trying to do other than something like so:
decltype(f(declval<C const>())) get_c() const { ... }