c++ container inside another container - c++

#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.

Related

What is the rule for type deduction for assignment using brace initialization?

In the following code, to my surprise, the { } appears to create a default initialized instance of int rather than of A.
struct A {
int i = 1;
A() = default;
A& operator=(const A& a) {
this->i = a.i;
return *this;
}
A& operator=(int i) {
this->i = i;
return *this;
}
};
int main() {
A a;
a = { };
return a.i;
}
What rule is used to parse a = { }? I thought that a = { } would have to be equivalent to a = decltype(a){ }.
What is the right term for the type of initialization that takes place here?
Here's an example on godbolt. At -O0 the assembly shows exactly which function is being used. Unless the int assignment operator is removed, A::operator=(int) is called.
Firstly, the braced-init-list {} could be used to initialize both int and A, which are also considered as conversion, and the conversion from {} to int wins in overload resolution, since the conversion from {} to A (via A::A()) is considered as a user-defined conversion.
Assignment operator is not special, compilers just collect all the viable operator=s in overload set, then determine which one should be selected by overload resolution. If you add another operator=(float) you'll get an ambiguous error since conversion from {} to int and from {} to float are both considered as standard conversion with same rank, which wins against user-defined conversion.

constexpr if and the return value optimization

I have this code:
#include <string>
class A {
public:
// A(A const &) = delete; // Code fails if this is uncommented.
explicit A(int);
explicit A(::std::string const &);
private:
::std::string myname_;
int foo_;
};
static constexpr bool which = false;
A test(::std::string const &s, int a)
{
if constexpr (which) {
A x{a};
return x;
} else {
A y{s};
return y;
}
}
This code fails if A has a deleted copy constructor. But, given the rules about return type for functions with if constexpr in them, it seems like the compiler should be applying RVO here.
Is there a reason why it isn't, other than it being an overlooked case in the language specification?
This has nothing to do with if constexpr
Simply this code is not allowed to compile:
class A {
public:
A(A const &) = delete;
explicit A(int);
};
A test(int a)
{
A x{a};
return x; // <-- error call to a deleted constructor `A(A const &) = delete;`
}
The changes in C++17 you are thinking of have to do with temporary materialization and don't apply to NRVO because x is not a prvalue.
For instance this code was illegal pre C++17 and now it is allowed:
A test(int a)
{
return A{a}; // legal since C++17
}
This is NRVO, which is non-mandatory copy elision:
(emphasis mine)
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a
function parameter or a catch clause parameter, and which is of the
same class type (ignoring cv-qualification) as the function return
type. This variant of copy elision is known as NRVO, "named return
value optimization".
This is an optimization: even when it takes place and the copy/move
(since C++11) constructor is not called, it still must be present and
accessible (as if no optimization happened at all), otherwise the
program is ill-formed:
BTW: Note that in your code, both the if part and else part of the constexpr if statement will be checked.
Outside a template, a discarded statement is fully checked. if
constexpr is not a substitute for the #if preprocessing directive:
void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}

C++ template: no matching function for call

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))

Templated class constructor uses wrong overload when in struct

The test is as following:
class NotInit{};
NotInit NOT_INIT;
template<class T>
class Optional{
T value;
bool has_value;
public:
Optional() : value(), has_value(false){}
explicit Optional(NotInit):value(), has_value(false) {}
explicit Optional(T const & val):value(val), has_value(true){}
explicit Optional(Optional<T> const & other) {}
Optional& operator=(Optional<T> const & other){}
Optional& operator=(T const & other){}
};
enum X {
FIRST
};
struct Some {
Optional<X> member;
};
int main(int, char**){
Optional<X> const opt(NOT_INIT);
Some s = {NOT_INIT};
return 0;
}
Clang 3.4 complains:
../st.cpp:31:12: error: no viable conversion from 'NotInit' to 'Optional<X>'
Some s = {NOT_INIT};
^~~~~~~~
1 error generated.
Why does it complain for the struct initialization but ont for the varaiable? Why is it not choosing the constructor with the proper parameter?
What overload is missing so that i can use this to init the struct?
I cannot use boost and i'm not sure this error would not appear if using boost::optional.
You are marking your constructors explicit. Thus when you are trying to initialize your struct Some with a brace-initializer list, you are triggering an implicit conversion...
This is prevented here:
class NotInit;
template <typename T>
class Optional {
// Here => Optional(NotInit) cannot be called implicitly
explicit Optional(NotInit):value(), has_value(false) {}
};
/* ... */
Some s = {NOT_INIT}; // Implicit call to Optional<X>(NotInit): whoops!
If you remove the explicit, you can keep:
Some s = {NOT_INIT};
If you chose not to, then you'll have to instantiate s like this:
Some s = {Optional<X>(NOT_INIT)};
In any case, you'll have to remove the explicit keyword on your copy constructor (which has to perform a copy and not be a "dummy" constructor).
Why? Because once the Optional<X> object is built from NOT_INIT, a copy has to be made to s.member.
Note: You could also provide a move constructor, which would be more appropriate in that case, since the object you get from the conversion of NOT_INITis an rvalue.

built-in data type conversion to user defined data type c++

My question is regarding data type conversion in c++. Does c++ provides a implicit conversion for built-in data types ( int, float) to user defined data types?
In the following example, I am trying to add a double ( t4 = t3 + 1.0) with test object type and its working fine using + operator so is the double implicitly converted to test type object?
class test {
double d;
int m;
public:
test() {
d=0;
m=0;
}
test(double n) {
d=n;
}
const test operator+(const test& t) {
test temp;
temp.d = d+ t.d;
return temp;
}
};
int main() {
test t1(1.2);
test t2(2.5);
test t3, t4;
t3= t1+ t2;
t4 = t3 + 1.0;
return 0;
}
The constructor test(double n) declares an implicit conversion from double to test. In general, any constructor that is callable with exactly one argument (that includes constructors that can take more arguments, but have default-values for those), can be used as an implicit conversion, unless it is marked explicit:
struct Foo {
Foo(int x); // implicit conversion from int to Foo
explicit Foo(char c); // marked explicit - no implicit conversion
Foo(std::string a, double pi = 3.14159); // can be used as implicit
// conversion due to default
// argument
};
Edit: t4 = 1.0 + t3 does not work, because you have overloaded your operator+ as a member-function, and thus it is only considered if the first operand is of the type test - implicit conversion are not tried in this case. To make this work, make your operator a free function:
test operator+(const test& lhs, const test& rhs);
Yes, as long as your constructor test(double) is not explicit
// implicite convertible
test(double n)
{
d=n;
}
// not implicite convertible
explicit test(double n)
{
d=n;
}