Please can someone help explain why I get an error when compiling the following code using Xcode 5.1 on OS X. Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn).
I want construct X below, passing it a vector of pairs.
#include <iostream>
#include <string>
#include <vector>
#include <utility>
struct X
{
public:
typedef std::vector<std::pair<std::string, std::string>> VectorType;
X(VectorType& params) : m_params(params)
{
}
VectorType m_params;
};
int main(int argc, const char * argv[])
{
X::VectorType pairs
{
{ "param-1", "some-string-1"}, // pair 0
{ "param-2", "some-string-2"}, // pair 1
{ "param-3", "some-string-3"}, // pair 2
{ "param-4", "some-string-4"}, // pair 3
{ "param-5", "some-string-5"}, // pair 4
{ "param-6", "some-string-6"}, // pair 5
{ "param-7", "some-string-7"} // pair 6
};
X x
{
{pairs[0], pairs[2], pairs[5]}
};
return 0;
}
The error reported is:
/main.cpp:37:7: error: no matching constructor for initialization of 'X'
X x
^
/main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: cannot convert initializer list argument to 'X'
struct X
^
/main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: cannot convert initializer list argument to 'const X'
struct X
^
/main.cpp:11:5: note: candidate constructor not viable: cannot convert initializer list argument to 'VectorType &' (aka 'vector<std::pair<std::string, std::string> > &')
X(VectorType& params) : m_params(params)
^
1 error generated.
Your constructor should take its argument by const reference
X(VectorType const & params)
^^^^^
Otherwise, you can't pass a temporary vector (as you try to do), since temporaries can't bind to non-const lvalue references.
X has 3 constructors:
Your user-defined one, which suppresses the automatic default-ctor:
X(VectorType& params)
The automatic copy-ctor and move-ctor:
X(X&&) noexcept
X(const X&)
The custom one expects an lvalue, never an xvalue or a constant object.
You probably want to split the ctor thus:
X(const VectorType& params) : m_params(params) {}
X(VectorType&& params) : m_params(std::move(params)) {}
Related
Previous SO post consulted: Default Constructor Calls
Here is the code:
#include <iostream>
#include <unordered_map>
using namespace std;
struct item {
std::string name;
int price;
// item() = default;
item(std::string n, int p): name(n), price(p) {}
};
void createItems(std::unordered_map<std::string, item>& blah);
int main()
{
std::unordered_map<std::string, item> blah;
createItems(blah);
cout << blah["armor"].name << " " << blah["armor"].price << std::endl;
return 0;
}
void createItems(std::unordered_map<std::string, item>& blah) {
item i1{"armor", 1000};
item i2{"amulet", 200};
blah["armor"] = i1;
blah["amulet"] = i2;
return;
}
The error message when run:
main.cpp:20:25: required from here
/usr/include/c++/11/tuple:1824:9: error: no matching function for call to ‘item::item()’
1824 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:5: note: candidate: ‘item::item(std::string, int)’
10 | item(std::string n, int p): name(n), price(p) {}
| ^~~~
main.cpp:10:5: note: candidate expects 2 arguments, 0 provided
main.cpp:6:8: note: candidate: ‘item::item(const item&)’
6 | struct item {
| ^~~~
main.cpp:6:8: note: candidate expects 1 argument, 0 provided
main.cpp:6:8: note: candidate: ‘item::item(item&&)’
main.cpp:6:8: note: candidate expects 1 argument, 0 provided
From my understanding, the default constructor is called but since I commented the line out the call results in an error.
Question: Why (and where) is the default constructor needed? My suspicion is that blah["armor"] calls the default constructor, but why?
As tkausl hinted at in their comment, the index operator is what needs the default constructor.
What the index operator does is
find tree location for key
if this location does not exist
create location
copy-construct key there
default-construct value there
return reference to value at location
Step 5 needs the default constructor. And since everything needs to be resolved at compile time, it needs the default constructor even if you never actually use a non-existent key. (You do, though.)
If you don't have a default constructor, you can't use the index operator. Use insert, emplace or try_emplace add elements to the map, and find or at to look them up.
struct Alien {
int id;
char const* name;
Alien() = default;
Alien(int id, char const* name) : id{id}, name{name} {}
Alien(int id): id{id} {}
};
struct Spaceship {
Alien alien1;
Spaceship() = default;
Spaceship(Alien const& z) {}
Spaceship(Spaceship const& other) = default;
Spaceship(Spaceship&& other) = default;
};
int main()
{
Spaceship s1(3433); // works
Spaceship s2(2322, "Kronas"); // error
}
source>:55:14: error: no matching constructor for initialization of 'Spaceship'
Spaceship s(2322, "Kronas");
^ ~~~~~~~~
<source>:45:5: note: candidate constructor not viable: requires single argument 'z', but 2 arguments were provided
Spaceship(Alien const& z) { }
^
<source>:46:5: note: candidate constructor not viable: requires single argument 'other', but 2 arguments were provided
Spaceship(Spaceship const& other) = default;
^
<source>:47:5: note: candidate constructor not viable: requires single argument 'other', but 2 arguments were provided
Spaceship(Spaceship&& other) = default;
^
<source>:44:5: note: candidate constructor not viable: requires 0 arguments, but 2 were provided
Spaceship() = default;
^
1 error generated.
Execution build compiler returned: 1
The std says that in direct initialization an implicit conversion to one of the T argument's constructor is applicable.
Why the second initialization with two arguments throws an error?
None of Spaceship constructors accepts two arguments. That is why it throws an error when you give it two. There should be something like:
Spaceship(int number, char const* name){
alien1.id = number;
alien1.name = name;
};
#include <bits/stdc++.h>
template <typename T>
struct Row {
Row() { puts("Row default"); }
Row(const Row& other) { puts("Row copy"); }
Row(Row&& other) { puts("Row move"); }
Row(T x) { puts("Row int"); }
};
int main() {
Row<Row<int>> x (10); // this works
Row<Row<int>> x2 = 10; // error
}
Why the code doesn't work? I know that first one performs direct initialization and the second one is copy initialization but still when it comes to container inside a container I doesn't get easy for me.
compiler: clang c++20
Error message:
no viable conversion from 'int' to 'Row<Row >'
Row<Row> x2 = 10
Problem is that copy initialization is less permissive than direct initialization:
> In addition, the implicit conversion in copy-initialization must produce T
directly from the initializer, while, e.g. direct-initialization expects
an implicit conversion from the initializer to an argument of T's constructor.
as initialization from int requires 2 conversions (from int to Row<int> and from Row<int> to Row<Row<int>>), direct initialization allows it but copy initialization does not. You can see it in documentation example as well:
struct S { S(std::string) {} }; // implicitly convertible from std::string
S s("abc"); // OK: conversion from const char[4] to std::string
S s = "abc"; // Error: no conversion from const char[4] to S
S s = "abc"s; // OK: conversion from std::string to S
In
Row<Row<int>> x2 = 10;
rather than assignment operator being invoked, the copy-initialisation happens which you mentioned as well. Copy-initialisation calls the copy (or move) constructor of the object, which requires you to pass the object by reference as an argument, which is clearly not possible with the int 10 that you are passing. To make it work, you would have to provide a valid type to copy from:
Row<Row<int>> x2 = Row<int>(10);
From the copy-constructor reference:
A copy constructor of class T is a non-template constructor whose first parameter is T&, const T&, volatile T&, or const vola
This is what compiler will probably generate(reference cppinsights) for the first call
/* First instantiated from: insights.cpp:12 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Row<int>
{
inline Row();
inline Row(const Row<int> & other);
inline Row(Row<int> && other);
inline Row(int x)
{
puts("Row int");
}
};
#endif
/* First instantiated from: insights.cpp:12 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct Row<Row<int> >
{
inline Row();
inline Row(const Row<Row<int> > & other);
inline Row(Row<Row<int> > && other);
inline Row(Row<int> x)
{
puts("Row int");
}
};
#endif
int main()
{
Row<Row<int> > x = Row<Row<int> >(Row<int>(10));
}
and your output will be
Row int Row int
on Row int from the first template instantiation and one from the second
When you are trying
Row<Row<int>> x2 = 10;
compiler converts 10 to int but it is enable to find such instantiation which can convert directly int to Row<Row<int>>. This will require 2 conversions.
So either you provide one conversion by yourself or use direct initialization.
I cannot understand why this program fails to compile both with g++ 7.3 or clang++ 5.0 using -std=c++14.
A can be initialized from a const int as shown. A const reference to A can also be create from a const int but call to f(const A &) with a const int fails. Why?
#include <iostream>
struct V {
int i;
template <class T>
V(const T &t) : i{t} {}
};
struct A {
int i;
A(V v) : i{v.i} {}
};
void f(const A &) {}
int main() {
const auto i = 42;
const A a1{i}; // OK
std::cout << a1.i << '\n'; // 42
const A &a2 = A{i}; // OK
std::cout << a2.i << '\n'; // 42
f(i); // no matching function for call to 'f'
return 0;
}
Given f(i);, copy initialization is applied. And i (with type const int) needs to be converted to A, two user-defined conversions are required; from const int to V, and from V to A. But only one user-defined conversion is allowed in one implicit conversion sequence.
Bot const A a1{i}; and const A &a2 = A{i}; are direct initialization, only one implicit conversion from i (with type const int) to the argument of A's constructor (i.e. V) is required, so they work fine.
Note the difference between copy initialization and direct initialization,
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.
As a workaround, you can perform explicit conversion on i before passing it to f().
Converting i to an A for the purpose of the function call will require two user defined conversions (int -> V -> A). The standard places a hard limit of a single user defined conversion on each implicit conversion sequence.
The same would happen if you were to try and bind a2 to i "directly". So you need to do a functional style cast (A{i}) when giving an argument to f as well.
You need two consecutive implicit type conversion here however C++ can do single conversion implicitley for you. If you want to let the compiler to generate the correct typed code for you, use template for the function f like following.
template <typename T>
void f(const T & x) { std::cout << x << std::endl;}
The reason why you need two type conversion is because of having only one constructor which takes type V in the struct. If you want to get rid of two type conversion as a second solution, you can add another constructor which takes int as a parameter like following
struct A {
int i;
A(V v) : i{v.i} {}
A(int theI) : i{theI} { }
};
Two user defined conversions are not supported in copy initialization. Simple work around to the problem is to wrap i with A while passing to funtion f with f(A(i))
I have coded a very basic class
class A
{
int n;
public:
A(int& val){val=n;}
A(const int& val=0){n=val;}
A(A& val){n=val.n;}//work without this constructor
};
int main()
{
A a=3;//want to call A::A(const int&)
return 0;
}
I don't want to create a constructor with a copy from an instance of A (for a future use)
What's wrong with this simple code?
Error message :
...\main.cpp||In function 'int main()':|
...\main.cpp|16|error: no matching function for call to 'A::A(A)'|
...\main.cpp|16|note: candidates are:|
...\main.cpp|11|note: A::A(A&)|
...\main.cpp|11|note: no known conversion for argument 1 from 'A' to 'A&'|
...\main.cpp|10|note: A::A(const int&)|
...\main.cpp|10|note: no known conversion for argument 1 from 'A' to 'const int&'|
...\main.cpp|9|note: A::A(int&)|
...\main.cpp|9|note: no known conversion for argument 1 from 'A' to 'int&'|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
It seems 3 is considered as an instance of A?
If I add A(const A& val){n=val.n;}
the constructor A(const int&) is chosen.
What can I do to compile successfully without A(const A& val)?
The issue is your copy constructor: A(A& val) { n=val.n; }.
Given this line A a = 3; one possibility is to use A a = A(3) which in turn will use the copy constructor. However, it is against the standard to bind a temporary to non-const. A proper copy constructor will solve the problem.
Example Code
#include <iostream>
class A
{
int n;
public:
A(int& val) : n(val) { std::cout << "A(int&)\n"; }
A(const int& val=0) : n(val) { std::cout << "A(const int&)\n"; }
A(const A& val) : n(val.n) { std::cout << "A(const A&)\n"; }
};
int main()
{
A a = 3;
return 0;
}
Example Output
A(const int&)
Live Example
Note:
The above code makes proper use of the initialization lists
The output shows the copy constructor is not actually invoked
The line of code;
A a = 3;
Is using copy initialisation to work. For that to work, a copy of A is needed and is duly made. You have two constructors taking an int as an argument (and neither are explicit).
The copy cannot bind to A(A& val) because normal references don't bind to temporary values.
Possible solutions, direct initialisation;
A a { 3 };
Add or change the copy constructor to a move constructor;
A(A&& val);
Add a const to the copy constructor (although this is not what you wanted, but it does work);
A(A const& val);
Note both clang and g++ reject the original code correctly, but VC++ accepts it.
In this statement
A a=3;//want to call A::A(const int&)
there is created a temporary object of type A using constructor
A(const int& val=0){n=val;}
However a temporary object can be bound to a constant reference. So the compiler need that the copy constructor
A( const A& val){n=val.n;}//work without this constructor
^^^^^
would be at least accessible (even if it will not be called due to the copy constructor elision).
But the class does not has this constructor and the compiler issues an error.
On the other hand when you comment this constructor then the compiler itself implicitly defines this constructor and the code is compiled.
Take into account that this constructor
A(int& val){val=n;}
^^^^^^
does not make sense.:)