Assignment of initializer list - c++

The code below is a minimal example of my problem. I created a simple template class containing a fixed-size array, and overloaded the assignment operator to accept any class defining the methods size() and begin() (eg, initializer_lists). I don't understand why g++ is not able to resolve my call to this operator (I'm using gcc 4.6):
***.cpp: In function ‘int main()’:
***.cpp:46:22: error: no match for ‘operator=’ in ‘a = {42, -1.0e+0, 3.14158999999999988261834005243144929409027099609375e+0}’
***.cpp:46:22: note: candidates are:
***.cpp:23:8: note: template<class U> A<T, N>::self& A::operator=(const U&) [with U = U, T = double, unsigned int N = 3u, A<T, N>::self = A<double, 3u>]
***.cpp:8:7: note: A<double, 3u>& A<double, 3u>::operator=(const A<double, 3u>&)
***.cpp:8:7: note: no known conversion for argument 1 from ‘<brace-enclosed initialiser list>’ to ‘const A<double, 3u>&’
***.cpp:8:7: note: A<double, 3u>& A<double, 3u>::operator=(A<double, 3u>&&)
***.cpp:8:7: note: no known conversion for argument 1 from ‘<brace-enclosed initialiser list>’ to ‘A<double, 3u>&&’
The first candidate is listed correctly, but there is no error message associated. Here is the code:
#include <iostream>
#include <algorithm>
#include <initializer_list>
// ------------------------------------------------------------------------
template <typename T, unsigned N>
class A
{
public:
typedef A<T,N> self;
// Default ctor
A() {}
// Copy ctor
template <typename U>
A( const U& other ) { operator=(other); }
// Assignemnt
template <typename U>
self& operator= ( const U& other )
{
if ( other.size() == N )
std::copy_n( other.begin(), N, m_data );
return *this;
}
// Display contents
void print() const
{
for ( unsigned i = 0; i < N; ++i )
std::cout << m_data[i] << " ";
std::cout << std::endl;
}
private:
T m_data[N];
};
// ------------------------------------------------------------------------
int main()
{
A<double,3> a;
a = {42,-1.0,3.14159};
a.print();
}
Does anyone know why this might be ambiguous, or what I did wrong?
Note: Ideally, I would even like to replace the first two lines of my main by a single one A<double,3> a = {42,-1.0,3.14159}; but I'm not sure how, I currently get the following error:
***: In function ‘int main()’:
***:45:34: error: could not convert ‘{42, -1.0e+0, 3.14158999999999988261834005243144929409027099609375e+0}’ from ‘<brace-enclosed initialiser list>’ to ‘A<double, 3u>’

Unlike auto, where a braced-init-list is deduced as an initializer_list, template argument deduction considers it to be a non-deduced context, unless there exists a corresponding parameter of type initializer_list<T>, in which case the T can be deduced.
From §14.8.2.1/1 [temp.deduct.call] (emphasis added)
Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list<P0> for some P0 and the argument is an initializer
list (8.5.4), then deduction is performed instead for each element of the initializer list, taking P0 as a function template parameter type and the initializer element as its argument. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5).
Thus the argument to your operator= is not deduced to be an initializer_list<double>. For the code to work, you must define an operator= that takes an initializer_list argument.
template <typename U>
self& operator= ( const std::initializer_list<T>& other )
{
if ( other.size() == N )
std::copy_n( other.begin(), N, m_data );
return *this;
}

A brace-enclosed initializer list does not necessarily have the type std::initializer_list<T>, so you need to specify that the assignment operator template expects an std::initializer_list:
template <typename U>
A& operator=(std::initializer_list<U> other )
{
if ( other.size() == N )
std::copy_n( other.begin(), N, m_data );
return *this;
}
or
A& operator=(std::initializer_list<double> other )
{
if ( other.size() == N )
std::copy_n( other.begin(), N, m_data );
return *this;
}
I must say, an assignment operator that silently fails if the sizes don't match doesn't seem like a great idea.

I'd say it's this:
A<double,3> a;
a = {42,-1.0,3.14159};
You are initializing a with default constructor, and then trying to use initializer list on already initialized object - which complains about lacking of appropriate operator= overload. Instead try:
A<double,3> a = {42,-1.0,3.14159};
EDIT:
You also didn't defined required constructor:
template <typename T, unsigned N>
class A
{
public:
A(std::initializer_list list) : m_data(list) {}
//...
}

Related

why is return SFINAE conversion operator not working?

struct A
{
template <typename T>
constexpr explicit operator
std::enable_if_t<
std::is_same<std::decay_t<T>, int>{},
int
>() const noexcept
{
return -1;
}
};
int main()
{
A a;
std::cout << int(a) << std::endl;
}
The error is clang-7.0.1:
<source>:21:16: error: no matching conversion for functional-style cast from 'A' to 'int'
std::cout << int(a) << std::endl;
^~~~~
<source>:7:22: note: candidate template ignored: couldn't infer template argument 'T'
constexpr explicit operator
^
That pattern just doesn't work for conversion functions. The problem is, in order to determine if a is convertible to int, we look for an operator int() - but what we get is:
std::enable_if_t<std::is_same<std::decay_t<T>, int>{}, int>
That's a non-deduced context - so we don't find int.
You have to move the condition into a defaulted parameter:
template <typename T, std::enable_if_t<std::is_same_v<T, int>, int> = 0>
constexpr explicit operator T() const noexcept { return -1; }
This way, we can deduce T and then let SFINAE do its magic. Note that you don't need decay since we don't have any kind of reference.

Variadic template: Perfect forwarding of integer parameter to lambda

There are similar questions, but I did not find an answer that works for my problem.
Consider the following code:
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <utility>
class TestClass
{
public:
TestClass( int value): mValue( value) { }
private:
int mValue;
};
template< typename T> class DeferredCreator
{
public:
template< class... Args> DeferredCreator( Args&&... args):
mpCreator( [=]() -> T*
{ return new T( std::forward< Args>( args)...); }
),
mpObject()
{ }
T* get() {
if (mpObject == nullptr)
mpObject.reset( mpCreator());
return mpObject.get();
}
private:
std::function< T*( void)> mpCreator;
std::unique_ptr< T> mpObject;
};
int main() {
DeferredCreator< int> dcInt( 42);
assert( dcInt.get() != nullptr);
return 0;
}
The idea is that the class DeferredCreator creates an object only when it is really needed. I got this work e.g. for strings, but I can't figure out how to pass a simple integer into my lambda.
The error message I get is:
prog.cpp:19:26: error: no matching function for call to 'forward'
{ return new T( std::forward< Args>( args)...); }
^~~~~~~~~~~~~~~~~~~
prog.cpp:36:27: note: in instantiation of function template specialization 'DeferredCreator<int>::DeferredCreator<int>' requested here
DeferredCreator< int> dcInt( 42);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/move.h:76:5: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/move.h:87:5: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^
2 errors generated.
I already tried to use decltype( args) as template argument for std::forward<>, but that did not help.
The code is also available here: https://ideone.com/MIhMkt
args... is constant because a lambda's call operator is implicitly const. So, if you make your lambda mutable, then it works:
[=]() mutable -> T*
{ return new T( std::forward< Args>( args)...); }
The reason it didn't work with decltype(args) is that the types themselves are not const, just the call operator.
The operator() of the closure type generated by your lambda expression is const-qualified. std::forward can attempt to move args..., which are data members of the closure. const objects cannot be moved.
You can mark your lambda as mutable:
mpCreator( [=]() mutable -> T*
{ return new T( std::forward< Args>( args)...); }
),
This removes the implicit const qualfiier from the closure type's generated operator().
live example on wandbox.org

std::bind on a generic lambda - auto type deduction

Consider the following code:
#include <iostream>
#include <functional>
int main() {
auto run = [](auto&& f, auto&& arg) {
f(std::forward<decltype(arg)>(arg));
};
auto foo = [](int &x) {};
int var;
auto run_foo = std::bind(run, foo, var);
run_foo();
return 0;
}
Which gives the following compilation error when compiled with clang:
$ clang++ -std=c++14 my_test.cpp
my_test.cpp:6:9: error: no matching function for call to object of type 'const (lambda at my_test.cpp:8:16)'
f(std::forward<decltype(arg)>(arg));
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:998:14: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const (lambda at my_test.cpp:8:16) &, const int &>' requested here
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:1003:2: note: in instantiation of default argument for 'operator()<>' required here
operator()(_Args&&... __args) const
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
my_test.cpp:11:12: note: while substituting deduced template arguments into function template 'operator()' [with _Args = <>, _Result = (no value)]
run_foo();
^
my_test.cpp:8:16: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
auto foo = [](int &x) {};
^
my_test.cpp:8:16: note: conversion candidate of type 'void (*)(int &)'
1 error generated.
Why is arg deduced to be const int& instead of just int&?
std::bind documentation says:
Given an object g obtained from an earlier call to bind, when it is
invoked in a function call expression g(u1, u2, ... uM), an invocation
of the stored object takes place, as if by std::invoke(fd,
std::forward(v1), std::forward(v2), ...,
std::forward(vN)), where fd is a value of type std::decay_t the
values and types of the bound arguments v1, v2, ..., vN are determined
as specified below.
...
Otherwise, the
ordinary stored argument arg is passed to the invokable object as
lvalue argument: the argument vn in the std::invoke call above is
simply arg and the corresponding type Vn is T cv &, where cv is the
same cv-qualification as that of g.
But in this case, run_foo is cv-unqualified. What am I missing?
MWE:
#include <functional>
int main() {
int i;
std::bind([] (auto& x) {x = 1;}, i)();
}
[func.bind]/(10.4) states that the cv-qualifiers of the argument passed to the lambda are those of the argument to bind, augmented by the cv-qualifiers of the call wrapper; but there are none, and thus a non-const int should be passed in.
Both libc++ and libstdc++ fail to resolve the call. For libc++, reported as #32856, libstdc++ as #80564. The main problem is that both libraries infer the return type in the signature somehow, looking like this for libstdc++:
// Call as const
template<typename... _Args, typename _Result
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
typename add_const<_Functor>::type&>::type>()(
_Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
std::declval<tuple<_Args...>&>() )... ) )>
_Result operator()(_Args&&... __args) const
During template argument deduction as necessitated by overload resolution, the default template argument will be instantiated, which causes a hard error due to our ill-formed assignment inside the closure.
This can be fixed by perhaps a deduced placeholder: remove _Result and its default argument entirely, and declare the return type as decltype(auto). This way, we also get rid of SFINAE which influences overload resolution and thereby induces incorrect behaviour:
#include <functional>
#include <type_traits>
struct A {
template <typename T>
std::enable_if_t<std::is_const<T>{}> operator()(T&) const;
};
int main() {
int i;
std::bind(A{}, i)();
}
This should not compile—as explained above, the argument passed to A::operator() should be non-const because i and the forwarding call wrapper are. However, again, this compiles under libc++ and libstdc++, because their operator()s fall back on const versions after the non-const ones fail under SFINAE.

boost optional and user-defined conversion

I am unable to write a correct user defined conversion for a type Item. This is what I've tried:
#include <iostream>
#include <boost/optional.hpp>
struct A
{
int x;
};
struct Item
{
boost::optional<int> x_;
Item(){}
Item(const A& s)
: x_(s.x)
{
}
operator boost::optional<A>() const {
boost::optional<A> s;
if (x_) {
s->x = *x_;
}
return s;
}
};
std::vector<A> getA(const std::vector<Item> &items) {
std::vector<A> a;
for (const auto &i : items) {
if (i.x_) {
a.push_back(*static_cast<boost::optional<A>>(i)); // <- this line causes error
}
}
return a;
}
That is how I use it:
int main() {
A a;
a.x = 3;
Item i(a);
auto v = getA({i});
return 0;
}
g++ -std=c++11 says:
In file included from /usr/include/boost/optional.hpp:15:0,
from test.cpp:2:
/usr/include/boost/optional/optional.hpp: In instantiation of ‘void boost::optional_detail::optional_base<T>::construct(const Expr&, const void*) [with Expr = Item; T = A]’:
/usr/include/boost/optional/optional.hpp:262:25: required from ‘boost::optional_detail::optional_base<T>::optional_base(const Expr&, const Expr*) [with Expr = Item; T = A]’
/usr/include/boost/optional/optional.hpp:559:78: required from ‘boost::optional<T>::optional(const Expr&) [with Expr = Item; T = A]’
test.cpp:30:55: required from here
/usr/include/boost/optional/optional.hpp:392:8: error: no matching function for call to ‘A::A(const Item&)’
new (m_storage.address()) internal_type(expr) ;
^
/usr/include/boost/optional/optional.hpp:392:8: note: candidates are:
test.cpp:3:8: note: A::A()
struct A
^
test.cpp:3:8: note: candidate expects 0 arguments, 1 provided
test.cpp:3:8: note: constexpr A::A(const A&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘const Item’ to ‘const A&’
test.cpp:3:8: note: constexpr A::A(A&&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘const Item’ to ‘A&&’
Why does it try to find A struct constructor instead of use user defined conversion operator?
You may point me directly to any position of the user-defined conversion page because I am unable to find any reason for this. For example,
User-defined conversion function is invoked on the second stage of the implicit conversion, which consists of zero or one converting constructor or zero or one user-defined conversion function.
in my opinion directly says that if no conversion constructor is defined then user-defined conversion function will be used. Am I wrong? And if yes, how can I implement user-defined conversion then without defining conversion cunstructor in struct A ?
You have two issues with your code. Your optional operator never initializes the boost::optional. If you don't do that, accessing members is undefined behavior. What you have to do is:
operator boost::optional<A>() const {
boost::optional<A> s;
if (x_) {
s = A{*x_};
}
return s;
}
The second issue is when you do:
static_cast<boost::optional<A>>(i);
That is equivalent to:
boost::optional<A> __tmp(i);
But it turns out that boost::optional has an explicit template constructor. That will be preferred to your conversion function. The error you're seeing is the compiling going down the path of this factory constructor, where Item is not such a factory.
You could simply use boost::optional<A> directly:
std::vector<A> getA(const std::vector<Item> &items) {
std::vector<A> a;
for (boost::optional<A> opt : items) {
if (opt) {
a.push_back(*opt);
}
}
return a;
}
Or, since the constructor template is explicit, you could use the conversion operator in a non-explicit context:
boost::optional<A> opt = i;
a.push_back(*opt);
This has the added benefit of also being easier to read.

Default template argument for function ignored

template < class A, class B, class R = A >
void addMultiplyOperation( std::function< R ( const A&, const B& ) > func )
{
...
}
addMultiplyOperation< float, int >( []( float a, int b ) { return a * b; } );
This gives the compiler error:
In function 'int main(int, char**)':
error: no matching function for call to 'addMultiplyOperation(main(int, char**)::__lambda1)'
addMultiplyOperation< float, int >( []( float a, int b ) { return a * b; } );
^
note: candidate is:
note: template<class A, class B, class R> void addMultiplyOperation(std::function<R(const A&, const B&)>)
void addMultiplyOperation( std::function< R ( const A&, const B& ) > func )
^
note: template argument deduction/substitution failed:
note: 'main(int, char**)::__lambda1' is not derived from 'std::function<R(const float&, const int&)>'
addMultiplyOperation< float, int >( []( float a, int b ) { return a * b; } );
^
Despite having the R template argument default initialised to A, I have to provide the third argument in order for this to compile. Is there something else I have to do in order to use default template arguments?
I'm using g++ v4.8.1.
Despite having the R template argument default initialised to A, I have to provide the third argument in order for this to compile.
Actually, this has nothing to do with the fact that it's a default argument. The compiler can't deduce A and B either. Take a look at this simple example:
template<class A>
void f(function<void(A)> f) { }
int main() {
auto lambda = [](){};
f(lambda);
}
You'd think this would be super easy, and A should be deduced as void. But nope, it can't be done. When deducing template parameters, the compiler doesn't consider what constructors the parameter type would have for each possible combination of template parameters. It would be intractable to perform this sort of deduction in general.
For now, you'll just have to make addMultiplyOperation accept any type, and hope it's callable...
template<class Function>
void addMultiplyOperation(Function func) {
// ....
}
If necessary, there are ways to deduce the types of the arguments that the function object can accept, for example as described in this answer: Is it possible to figure out the parameter type and return type of a lambda?
This will lead to some nasty compilation errors if the object passed in is not actually callable, or takes the wrong number of arguments. For now I'm not sure whether there's a nice way to solve this. Concepts, coming in C++14, should alleviate some of these issues.