#include <array>
#include <vector>
#include <cinttypes>
#include <iostream>
using namespace std;
template<size_t N>
struct item_t {
array<uint32_t, N> weight = {0};
};
int main(void) {
vector<item_t<3>> items;
items.emplace_back({{9,2,3}});
cout << items[0].weight[0] << endl;
return 0;
};
I'm at a bit of a loss here. Error is on the emplace_back line and no idea how to resolve it. Any help or hints would be appreciated, thanks.
EDIT
gcc version 4.8.2
$ g++ -std=c++11 test.cpp
test.cpp: In function ‘int main()’:
test.cpp:16:30: error: no matching function for call to ‘std::vector<item_t<3ul> >::emplace_back(<brace-enclosed initializer list>)’
items.emplace_back({{9,2,3}});
^
test.cpp:16:30: note: candidate is:
In file included from /usr/include/c++/4.8/vector:69:0,
from test.cpp:2:
/usr/include/c++/4.8/bits/vector.tcc:91:7: note: void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = item_t<3ul>; _Alloc = std::allocator<item_t<3ul> >]
vector<_Tp, _Alloc>::
^
/usr/include/c++/4.8/bits/vector.tcc:91:7: note: candidate expects 0 arguments, 1 provided
The problem is with the struct initialization = {0} and with emplace_back.
emplace_back() uses template argument deduction to determine the types of the elements passed to the function. A brace enclosed initializer list is not an expression and doesn't have type and therefore cannot be deduced by the template. You have to explicitly call the constructor here:
items.emplace_back(item_t<3>{{1,2,3}});
There are two issues here :
Trying to init a object of type T like this T{...} is referred to as aggregate initialization. Under some conditions, there is a default behaviour specified for it, even if you don't have a constructor which accepts a initializer_list. In C++11, you are not allowed to provide non-default constructors or in-class initializers. So, given this definition
template<size_t N>
struct item_t {
array<uint32_t, N> weight = {0};
};
you cannot write item_t<3> t{1,2,3};.
That, however, isn't your problem. The reason your code fails is that emplace_back tries to forward the arguments to a constructor of the vectors underlying type. In your case, there isn't a match. Note that nice a braced-init list isn't equivalent to an initializer_list in this context, you cannot solve this problem by adding an initializer_list constructor and will have to help the compiler out some other way.
Related
I am missing something with std::make_shared. Can't it resolve the type of a std::initializer_list, or am I doing something else wrong?
#include <vector>
#include <memory>
class A {};
int main()
{
A a;
std::vector<A> veca{A(), A{}, a}; // this works ofc
std::vector<A> vecb({A(), A{}, a}); // this too
std::make_shared<std::vector<A>>(vecb); // and this, ofc
std::make_shared<std::vector<A>>({a}); // what's wrong here?
return 0;
}
Error:
main.cpp:21:41: error: too many arguments to function ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = std::vector; _Args = {}]’
std::make_shared<std::vector<A>>({a});
^
In file included from /usr/include/c++/6/memory:82:0,
from main.cpp:10:
/usr/include/c++/6/bits/shared_ptr.h:632:5: note: declared here
make_shared(_Args&&... __args)
^~~~~~~~~~~
Live example: https://onlinegdb.com/r1DlHquDL
Consider the following minimal example of your problem:
template <typename... Ts>
void f(Ts&&...); // replacement for std::make_shared
int main()
{
f({1});
}
This case is described in the C++ Standard in [temp.deduct.call/1]:
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction 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<P′> or P′[N] for some P′ and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list independently, taking P′ as separate function template parameter types P′i and the ith initializer element as the corresponding argument. In the P′[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context ([temp.deduct.type]).
In your case, the last sentence applies. Interestingly, the error message says something else with GCC, which is weird. With Clang, the error message is clear:
error: no matching function for call to 'f'
note: candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter 'Ts'
I am trying to initialize a std::map with an initializer list (in production, this is a member initializer of a class, but my minimal failing example is below). Given
#include <map>
struct Cfg {};
struct Alg
{
explicit Alg(Cfg const&) {}
};
using MyMap = std::map<int, Alg>;
int main()
{
Cfg cfg;
MyMap m = {
{1, {cfg}}, // error #1
{2, cfg}, // error #2
{3, Alg(cfg)}, // works fine
};
return 0;
}
When compiling, error #1 is:
foo.cc: In function ‘int main()’:
foo.cc:22:5: error: converting to ‘const Alg’ from initializer list would use
explicit constructor ‘Alg::Alg(const Cfg&)’
};
^
This is pretty straightforward. Passing a Cfg to the initializer requires conversion and the explicit constructor prohibits it.
Error #2 is
foo.cc: In function ‘int main()’:
foo.cc:22:5: error: converting to ‘std::pair<const int, Alg>’ from initializer list would use explicit constructor ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U1 = int; _U2 = Cfg&; typename std::enable_if<(std::_PCC<true, _T1, _T2>::_MoveConstructiblePair<_U1, _U2>() && (! std::_PCC<true, _T1, _T2>::_ImplicitlyMoveConvertiblePair<_U1, _U2>())), bool>::type <anonymous> = 0; _T1 = const int; _T2 = Alg]’
};
^
This is a little more confusing. I think the error talks about implicitly invoking an explicit std::pair constructor; reading the documentation for std::pair, I get lost in which constructors are explicit when.
The third case is explicit construction. For reasons of maintainability, I'd rather not do that.
It seems like I've read that many initializer list counter-intuitive issues are solvable by adding more braces (gross simplification) but I confess I'm lost at this point.
If I remove the explicit qualification of the Alg constructor, all three cases compile. However, I'm not sure it makes sense to provide implicit conversion just to simplify an initializer list.
Is there a way to initialize my map elements without explicitly constructing the Alg members? Using g++ 7.3
As far as I know, there is no way around specifying the Alg type. This is the intention of explicit constructors anyhow. Just for the sake of [I don't know what to say here], you can nevertheless invoke std::pair's in-place constructor like the following.
MyMap m{{std::piecewise_construct, std::forward_as_tuple(1), std::tie(cfg)}};
This way, you don't have to type Alg, which kind of answers your questions. Besides, do the above only if you hate yourself and your co-workers.
Note: the in-place constructor of std::pair is actually present to allow for non-copyable, non-movable types.
I have confusing situation with simple code:
struct Item {
size_t span{};
};
int main() {
Item item{1}; // error is here
return 0;
}
While compiling this I have following error:
test.cpp: In function ‘int main()’:
test.cpp:8:13: error: no matching function for call to ‘Item::Item(<brace-enclosed initializer list>)’
Item i{1};
^
test.cpp:8:13: note: candidates are:
test.cpp:3:8: note: constexpr Item::Item()
struct Item {
^
test.cpp:3:8: note: candidate expects 0 arguments, 1 provided
test.cpp:3:8: note: constexpr Item::Item(const Item&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘int’ to ‘const Item&’
test.cpp:3:8: note: constexpr Item::Item(Item&&)
test.cpp:3:8: note: no known conversion for argument 1 from ‘int’ to ‘Item&&’
Why g++ tries to find a ctor for initializer list in this case instead of simple C-style structure object creating?
If I remove {} from size_t span{} it compiles successfully.
It also happens if I change the line to size_t span = 0 so it seems to be some initialization in declaration issue which exists since c++11.
Usign Item item{1}; means you're doing list-initialisation (of item). List initialisation is defined as follows:
if the type is an aggregate, aggregate initialisation (what you refer to as "C-style struct object creating") happens
...
if the type is a class, constructors are considered
Your class has no constructors. It is also not a (C++11) aggregate, because it contains an initialiser for a non-static data member.
Note that this restriction (member initialisers) was lifted in C++14, so Item is a C++14 aggregate and your code, while not valid C++11, is valid C++14.
Consider this short program compiled with GCC 4.7.2 g++ -std=c++11 test.cc
#include <memory>
#include <queue>
struct type{
type(int a) : v(a) {}
int v;
};
typedef std::shared_ptr<type> type_ptr;
int main(){
int value = 3;
std::queue<type_ptr> queue;
auto ptr{std::make_shared<type>(value)};
queue.push(ptr);
}
The compiler outputs the following errors:
src/test.cc: In function ‘int main()’:
src/test.cc:15:17: error: no matching function for call to ‘std::queue<std::shared_ptr<type> >::push(std::initializer_list<std::shared_ptr<type> >&)’
src/test.cc:15:17: note: candidates are:
In file included from /usr/include/c++/4.7/queue:65:0,
from src/test.cc:2:
/usr/include/c++/4.7/bits/stl_queue.h:211:7: note: void std::queue<_Tp, _Sequence>::push(const value_type&) [with _Tp = std::shared_ptr<type>; _Sequence = std::deque<std::shared_ptr<type>, std::allocator<std::shared_ptr<type> > >; std::queue<_Tp, _Sequence>::value_type = std::shared_ptr<type>]
/usr/include/c++/4.7/bits/stl_queue.h:211:7: note: no known conversion for argument 1 from ‘std::initializer_list<std::shared_ptr<type> >’ to ‘const value_type& {aka const std::shared_ptr<type>&}’
/usr/include/c++/4.7/bits/stl_queue.h:216:7: note: void std::queue<_Tp, _Sequence>::push(std::queue<_Tp, _Sequence>::value_type&&) [with _Tp = std::shared_ptr<type>; _Sequence = std::deque<std::shared_ptr<type>, std::allocator<std::shared_ptr<type> > >; std::queue<_Tp, _Sequence>::value_type = std::shared_ptr<type>]
/usr/include/c++/4.7/bits/stl_queue.h:216:7: note: no known conversion for argument 1 from ‘std::initializer_list<std::shared_ptr<type> >’ to ‘std::queue<std::shared_ptr<type> >::value_type&& {aka std::shared_ptr<type>&&}’
Indicating that the auto type is expanded to an initializer list instead of std::shared_ptr<type>; in fact replacing {...} with = ... makes the code compile as auto expands to the correct type.
I'm a bit suprised that this seemingly obvious use case fails to achieve the expected result. Especially as I recall the new bracket initialization syntax to be touted as the end-all, be-all solution to initializing problems.
So my question is: Was this intended in the standard? Or is it an oversight or even a gcc bug? Or am I just thinking about it wrong?
As Xeo says in his comment, this is standard behavior. 7.1.6.4 auto specifier [dcl.spec.auto] para 6 specifies:
Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction. Let T be the type that has been determined for a variable identifier d. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list (8.5.4), with std::initializer_list<U>. The type deduced for the variable d is then the deduced A determined using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer for d is the corresponding argument. If
the deduction fails, the declaration is ill-formed.
It is also widely despised - there's a proposal under review by the committee to change the behavior for C++14. C++14's support for generalized lambda capture exacerbates the problem.
Update: In Urbana (See CWG Motion 16 in N4251 WG21 2014-11 Urbana Minutes) the committee applied N3922 New Rules for auto deduction from braced-init-list to the C++17 Working Paper. They decided to fix the special case that allows auto to deduce an initializer_list by adding another special case. auto works the same way for copy-list-initialization, but for direct-list-initialization from a braced-init-list with a single element auto deduces from that element directly. direct-list-initialization from a multiple-element braced-init-list is now ill-formed.
That means that given
auto x = {42};
x has type std::initializer_list<int>, but in
auto x{42};
x is an int.
I was experimenting with tuples and encountered a problem with creating tuples.
The code example is as follows.
//a.cpp
#include <tuple>
using namespace std;
int main() {
auto te = make_tuple(); //this line is ok
auto tte = make_tuple(te); //this line gives an error.
return 0;
}
I compiled it with both g++ 4.5 (g++ -std=c++0x a.cpp) and MS VC++2010.
Both compilers are giving me an error on the second line in main().
My question is this:
Since 'te' is a well-defined variable, why can't another tuple be created with te being the content. Is this semantic correct?
I guess this is kind of a boundary case, but if the arithmetic is correct, zero should be allowed, IMHO.
FYI, the error message from gcc is:
$ gcc -std=c++0x a.cpp
In file included from a.cpp:1:0:
c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/tuple: In constructor
'std::tuple<_Elements>::tuple(std::tuple<_UElements ...>&) [with _UElements = {},
_Elements = {std::tuple<>}]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/tuple:551:62: instantiated from
'std::tuple<typename std::__decay_and_strip<_Elements>::__type ...>
std::make_tuple(_Elements&& ...) [with _Elements = {std::tuple<>&}, typename
std::__decay_and_strip<_Elements>::__type = <type error>]'
a.cpp:6:27: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.2/include/c++/tuple:259:70: error: invalid
static_cast from type 'std::tuple<>' to type 'const std::_Tuple_impl<0u>&'
This looks like the compiler has matched your std::tuple<> against the following constructor of std::tuple<std::tuple<>> (See 20.4.2p15-17 in N3242):
template <class... UTypes> tuple(const tuple<UTypes...>& u);
Requires:
sizeof...(Types) == sizeof...(UTypes).
is_constructible<Ti , const Ui &>::value is true for all i.
Effects:
Constructs each element of *this with
the corresponding element of u.
Remark: This constructor shall not
participate in overload resolution
unless const Ui & is implicitly
convertible to Ti for all i.
I think this is a bug in the implementation of std::tuple from your compiler; the "remark" implies that this constructor should not be considered, since it won't compile.