Rule of thumb for using auto&& and const auto & - c++

I have studied auto and I know it deduces types from initialized values. If I tried to write some guidelines for usage of auto, can I put below statements as rules?
Use auto && for all values (r-values and l-value, as it is an universal ref) that are modifiable,
auto && even for read only values,
Use auto wherever possible (individual preference for coding style).
EDIT
class Test
{
public:
Test() = default;
Test(const Test&) = default;
Test(Test&&) = default;
int i = 1;
};
Test getObj()
{
return Test();
}
Test& getObjByRef()
{
static Test tobj;
return tobj;
}
const Test getObjConst()
{
return Test();
}
int main()
{
auto && obj1 = getObj();
obj1.i = 2;
std::cout << " getObj returns: " << getObj().i << std::endl;
auto&& obj2 = getObjByRef();
obj2.i = 3;
std::cout << " getObjByRef returns: " << getObjByRef().i << std::endl;
auto && obj3 = getObjConst();
//obj3.i = 4; => //Error C3490 'i' cannot be modified because it is being accessed through a const object
return 0;
}
So in above example i used auto && for all three functions
getObj
getObjByRef
getObjConst
and it works as expected.
Now can I conclude that:
auto && can be used to hold(initialize) any value, OR
We can use auto && every possible place.
Do you see any pitfall of this approach?

1) Yes
2) Yes
3) As you said, 'individual preference for coding style'

Related

How to make a template Wrapper/Decorator in c++17

Dear Stackoverflow community,
I'm still bit fresh in c++ and I've been scratching my head and haven't found a solution to my problem yet. I've been searching and trying things for a while now and I've gotten to the point where asking a question would be more beneficial and educational.
Problem:
I'd like to make a class or function that wraps/decorates a given function with or without parameters.
Like a good old fashioned #wrapthis in python or c# and the like.
The closest thing I found so far (that is elegant, short and easy to use) is from this stackoverflow answer: C++ Equivalent Decorator
The scratching-my-head part is trying to pass a pointer-function. The error I'm receiving:
Error (active) E0300 a pointer to a bound function may only be used to call the function
Which obviously means that somehow passing a pointer in this fashion is not allowed, so what are my options here?
A working example as answer would be great!
Example of what I'm trying to achieve can be found below:
Something.h
class Something
{
private:
public:
Something() {}
void v_func_with_nothing() { std::cout << "v_func_with_nothing" << "\n"; }
void v_func_with_void(void) { std::cout << "v_func_with_void" << "\n"; }
void v_func_with_one_arg(int x) { std::cout << "v_func_with_one_arg" << x << " " << "\n"; }
void v_func_with_args(int x, int y) { std::cout << "v_func_with_args" << x << " " << y << "\n"; }
int return_func_with_nothing() { return 1; }
int return_func_with_void(void) { return 3; }
int return_func_with_one_arg(int x) { return x; }
int return_func_with_args(int x, int y) { return x+y; }
};
Decorator.h [Again source: C++ Equivalent Decorator]
template<typename T>
auto decorator(T&& func)
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END decorating\n";
return result;
};
return new_function;
}
main.cpp
#include <iostream>
#include "Something.h"
#include "Decorator.h"
int main()
{
Something* something = new Something();
auto somedeco = decorator(&something->return_func_with_one_arg);//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
Thank you!
EDIT: SOLUTION
Based on the kind answers given down below I thought of editing this post with an example. The solution to the problem was using lambda.
Decorator.h: I created 2 decorators (one for return-functions, one for void-functions):
template<typename T>
auto DECO_R(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN RETURN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END RETURN decorating\n";
return result;
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
template<typename T>
auto DECO_V(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN VOID decorating...\n";
func(std::forward<decltype(args)>(args)...);
std::cout << "END VOID decorating\n";
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
Main.cpp: 2 examples
int main()
{
Something* something = new Something();
auto somedeco = DECO_R(
[&](int x) {
return something->return_func_with_one_arg(x);
});
int value = somedeco(255);
std::cout << value << "\n";
auto some_v_deco = DECO_V(
[&](int x) {
return something->v_func_with_one_arg(x);
});
some_v_deco(2);
return 0;
}
Output
BEGIN RETURN decorating...
END RETURN decorating
255
BEGIN VOID decorating...
v_func_with_one_arg2
END VOID decorating
I hope this helps others out there.
The call decorator(&something->return_func_with_one_arg) is invalid. There's no such thing as a pointer to a bound function in C++.
If you want somedeco to be a function-like object that wraps a call to something->return_func_with_one_arg(42), for example, you will need to wrap the call either in a lambda:
auto somedeco = decorator(
[&]() {
return something->return_func_with_one_arg(42);
}
);
somedeco();
Or you could pass the parameter through the decorator:
auto somedeco = decorator(
[&](int x) {
return something->return_func_with_one_arg(x);
}
);
somedeco(42);
Keep in mind that this will require that the object pointed to by something outlives the object returned by decorator.
There is no simple 1:1 replacement for Pythons dynamic typing in C++. Your example does not compile, because there are no pointer to member function of one specific instance. Pointer to member functions always need an instance to be called. For one simple case I would suggest to use a lambda:
int main() {
Something something;
auto somedeco = [&](auto param) {
// before call
auto v = something.return_func_with_one_arg(param);
// after call
return v;
};
return somedeco(1);
}
As you can see the whole machinery of decorate isn't really needed, because with a lamdda you can write the wrapped function inline. On the other hand, the decorator allows you to reuse // before call and // after call for different methods. To fix your code you could also pass the lambda to decorate.
PS: Don't use new to create objects.
you need bind it
int main()
{
Something* something = new Something();
using std::placeholders::_1;
auto f = std::bind( &Something::return_func_with_one_arg, something, _1 );
auto somedeco = decorator( f );//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
https://godbolt.org/z/zdYW9q

Detect initialized variables

Is there a way to check if certain variable is initialized before some point in a program?
For example, how to check if certain variable is initialized somewhere before the IfStmt node?
Methods from VarDecl class (hasInit() and getInit()) are not enough because of the following situation:
int x = 0; // hasInit() return true
int y;
...
y = 0; // initialized here, but hasInit() returns false
...
if (...) {}
If you maintain a product written by C++ code and hope to remove ugly indeterminate variables, a reasonable way to do it is defining an initializing function or lambda f, and then declare a local variable as const auto x = f(...); from the get-go.
OTOH, if you delay the value asignment on purpose, there are several methods to detect the value is assigned or not.
I just came up with following methods.
std::optional
In C++17 and over,
std::optional<T> enables us to detect whether values are assigned or not.
std::optional::has_value and std::optional::value correspond to your hasInit and getInit respectively as follows:
DEMO
#include <iostream>
#include <optional>
template<typename T>
void checkInitialization(const std::optional<T>& a)
{
if(a.has_value()){
std::cout << "Value is assigned by " << a.value() << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::optional<int> x;
checkInitialization(x); // Value is still not assigned
x = 1;
checkInitialization(x); // Value is assigned
return 0;
}
The output is as follows:
Value is still not assigned.
Value is assigned by 1.
std::unique_ptr
We can also check it using std::unique_ptr<T> which is introduced from C++11.
First we define a variable as std::unique_ptr<T> x; where (x == nullptr) is still true.
Later on, we assign a value by x = std::unique_ptr<int>(new int(1)) and then (x == nullptr) becomes false.
(In C++14 x = std::make_unique<int>(1) works and is simple.)
Thus we can again get the previous output with the following code:
DEMO
#include <iostream>
#include <memory>
template<typename T>
bool hasInit(const std::unique_ptr<T>& a)
{
return (a != nullptr);
}
template<typename T>
const T& getInit(const std::unique_ptr<T>& a)
{
return *a;
}
template<typename T>
void checkInitialization(const std::unique_ptr<T>& a)
{
if(hasInit(a)){
std::cout << "Value is assigned by " << getInit(a) << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::unique_ptr<int> x;
checkInitialization(x); // Uninitialized
x = std::unique_ptr<int>(new int(1));
//x = std::make_unique<int>(1); // C++14
checkInitialization(x); // Initialized
return 0;
}
std::pair
We can also apply std::pair<bool, T> where std::pair::first and std::pair::second correspond to your hasInit and getInit respectively.
We again get the previous output:
DEMO
#include <iostream>
#include <utility>
template<typename T>
void checkInitialization(const std::pair<bool, T>& a)
{
if(a.first){
std::cout << "Value is assigned by " << a.second << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::pair<bool, int> x{false, 0};
checkInitialization(x); // Uninitialized
x = {true, 1};
checkInitialization(x); // Initialized
return 0;
}
Firstly as mentioned in the comments:
int y = 0; // initialization
int y; y = 0; // assignment
Let's assume you want to detect assignment. One simple way could be wrap the integer you want to track in a struct and write a custom operator = (int). For example:
struct Foo
{
Foo() {std::cout << "default init" << std::endl;}
Foo& operator = (int elem)
{
cout<<"Int-Assignment operator called "<<endl;
x = elem;
is_assigned = true;
return *this;
}
int x = 0; // default initialized to 0
bool is_assigned = false; // default initialized to false
};
Now let's see what happens:
int main()
{
Foo t1;
// t1.is_assigned is false
t1 = 0;
// t1.is_assigned is true
return 0;
}
You could use something like this or a variant if needed. Here's the code running online corresponding to the above.
Is this what you wanted?

Should we capture by const reference in lambda?

I have been reading Lambda capture as const reference?
It is an interesting feature, and sometimes I also wish this feature to exist, especially when I have huge data, which I need to access inside a lambda function.
My follow up questions -
Should we capture by const reference in lambda? If yes, how should it be behave?
(Edit - I am also interested in the behaviour of the lifetime of the captured variable.)
Is there any possible downside, by introducing it in the C++ grammar? (I can't think of any)
Let us assume we can.
We assume [const &] as syntax to capture.
int x = 10;
auto lambda = [const & x](){ std::cout << x << std::endl; };
lambda(); // prints 10, great, as expected
x = 11;
lambda(); // should it print 11 or 10 ?
My intuition is that is should behave just like [&] but the captured value should not be allowed to modify.
template<typename Func>
void higher_order_function(int & x, Func f)
{
f(); // should print 11
x = 12;
f(); // should print 12
}
void foo()
{
int x = 10;
auto c = [const & x] () { std::cout << x << std::endl; };
c(); // should print 10
x = 11;
c(); // should print 11
higher_order_function(x, c);
auto d = [const & x] () { x = 13; }; // Compiler ERROR: Tried to assign to const qualified type 'const int &'!
}
I've been wondering about this myself.
Since the operator() is const by default, I would assume that allowing const references is acceptable as well.
With the current standards(C++17), the closest I got to this behaviour is:
auto c = [ &x = std::as_const(x) ] () { std::cout << x << std::endl; };
Workaround in C++11/C++14 would be(thanks to Daniel for suggestion):
auto const & crx = x;
auto c = [ &crx ] () { std::cout << crx << std::endl; };
lambda(); // should it print 11 or 10 ?
I don't understand why it should print 10. Consider lambda being just an instance of some anonymous class. Making it a normal class, it should look like:
class Lambda {
public:
Lambda(const int & i) : i_(i) { }
void operator()() { std::cout << i_ << std::endl; }
private:
const int & i_;
};
int x = 10;
Lambda lambda(x);
lambda(); // prints 10, great, as expected
x = 11;
lambda(); // should it print 11 or 10 ?
The meaning of const reference here is only that you cannot modify x through i_ member reference variable.
A simlpler scenario:
int x = 10;
const int & crx = x;
x++;
std::cout << crx << std::endl; // prints 11
Maybe not exactly what you're looking for but... I suppose you can pass through a function that receive the same value by const reference.
Something as follows
template <typename T>
auto make_capture_const (T const & x)
{ return [&x](){ std::cout << x << std::endl; }; }
// ...
int x { 42 };
auto l = make_capture_const(x);
l();
If you try to modify x inside the lambda
std::cout << x++ << std::endl;
you should get an error.
As you can see, from, this solution you get that x can't be modified inside the lambda but the lambda is influenced from the value changes outside
int x { 42 };
auto l = make_capture_const(x);
l(); // print 42
x = 43;
l(); // print 43
IMHO, an hypothetic [const &] capture syntax should works in the same way. But I understand that is highly questionable.

How to handle multi-type variables with templates?

I was trying to make a function that assigns y to x regardless whether x, y are int or std::string. I wrote this code:
#include <iostream>
#include <string>
#include <typeinfo>
template <typename T>
T& assign(T& x, T& y){
if ( typeid(x).name() == "Ss" && typeid(y).name() == "Ss" ){
std::string k = static_cast<std::string>(y);
x = k;
return x;
}
else if ( typeid(x).name() == "i" && typeid(y).name() == "i" ){
int k = static_cast<int>(y);
x = k;
return x;
}
else{
std::cout << "uncorrect assignment" << std::endl;
}
}
int main(){
std::string a = "empty_string";
std::string b = "Hi there";
assign(a, b);
std::cout << a << std::endl;
}
But it doesn’t work.
It gives the error:
[Error] invalid static_cast from type ‘std::basic_string<char>’ to type
at line 14:
int k = static_cast<int>(y);
I can’t understand, what is the problem?
I know the objection: I might have just defined function assign as:
template <typename T>
T& assign(T& x, T& y){
x = y;
}
which works. However, I was working on an other more complex function on which I have to (or at least I haven’t found any way other than) use static_cast.
So, if you could, please, explain to me what is the mistake in this example, I may try to fix the function I am working on.
Thank you very much,
Simone.
To do what do you want, you need C++17 and if constexpr. And the use of something that works compile-time, not of typeid that works runtime.
The problem is that with your code, typeid permit, runtime, to choose the if or the else part of your code, but the compiler must compile both part. So must compile
int k = static_cast<int>(y);
x = k;
when T is std::string. This give an error.
You need a type-traits (std::is_same, by example), that is evaluated compile-time, and a construct that avoid the compilation of the wrong part. This construct is if constexpr ( <test> ) (where the <test> is valuable compile time) but, unfortunately, is available only from C++17.
So, in C++17 you can write
template <typename T>
void assign (T & x, T const & y)
{
if constexpr ( std::is_same<T, std::string>::value ) {
std::string k = static_cast<std::string>(y);
x = k;
}
else if constexpr ( std::is_same<T, int>::value ) {
int k = static_cast<int>(y);
x = k;
}
else {
std::cout << "uncorrect assignment" << std::endl;
}
}
but, pre C++17, you have to follows different ways.
To handle different types separately inside a function, an option is to define a local struct with overloaded function call operators to different types:
#include <iostream>
#include <string>
template<typename T>
T& assign(T& x, const T& y) {
struct {
void operator()(std::string& lhs, const std::string& rhs) {
std::cout << "Type is std::string." << std::endl;
lhs = rhs;
}
void operator()(int& lhs, const int& rhs) {
std::cout << "Type is int." << std::endl;
lhs = rhs;
}
} assign_impl;
assign_impl(x, y);
return x;
}
int main() {
/* Test No. 1 */ {
std::string dest, src = "Foo";
std::cout << "Test result: " << assign(dest, src) << std::endl;
}
/* Test No. 2 */ {
int dest, src = 32;
std::cout << "Test result: " << assign(dest, src) << std::endl;
}
}
The code above will work on C++98 and above but its disadvantage is that it will raise compiler errors if you try to use it with unhandled types.

Behavior with std::erase() non-references vs reference

Suppose I have a vector of type my_object which has a size of 3 and I want to get 3 elements from my vector storing them in a reference
Then I want to remove and erase element_3 by using std::remove_if() and element_1 and element_2 by using std::remove
Here is my_object:
class my_object {
public:
my_object(int num);
bool exists() const;
private:
int num;
};
my_object::my_object(int num) : num(num) {}
bool my_object::exists() { return num == 1; }
Here is main:
std::vector<my_object> my_vector;
int main() {
my_object e1(2);
my_object e2(2);
my_object e3(1); // i.e exists() will return true in lambda
my_vector.push_back(e1);
my_vector.push_back(e2);
my_vector.push_back(e3);
const auto& element_1 = my_vector.at(0);
const auto& element_2 = my_vector.at(1);
const auto& element_3 = my_vector.at(2);
auto lambda = [](auto& src) { return src.exists() };
std::erase(std::remove_if(b, e, lambda), e); // remove_if for element_3
std::erase(std::remove(b, e, element_1), e);
std::erase(std::remove(b, e, element_2), e);
return 0;
}
What is extremely weird is that when I declare element_1, element_2, element_3 by reference than the erasing isn't done properly and the size isn't decreased to 0, but when I write const auto with no & then it works perfectly fine, can anyone explain this weird behavior to me?
Discounting the methods of erasure, those references are just that: references to objects living in the container. Once remove or remove_if have performed their tasked move-assignment while marching up the sequence, those references are still referring to the same elements, but the occupants of those slots are:
At best, still valid objects because a valid object either stayed where it was, or one was move-assigned there.
Mere shells of some former self because the reference now refers to a source object that was never reclaimed by a targeted move.
I'm not going to dive into std::remove, Rather. Look at this rather trivial example of std::remove_if
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
Output
1 3 5
1 4 5
As you can see, the functional description of std::remove_if lives up to what you see in the code. The 3 element was removed, and the 4 element was move-assigned to its place. What you don't see here is that the 5 element was move-assigned to the 4's place, and the 5 value you see here now happens to be coming from the slot where 5 was. The standard says that object is "valid", but with an "unspecified" value. We can verify that by ensuring our move-source of a move-assignment is, in fact, "invalid" (as far as we're concerned). Modifying our original program gives us this:
#include <iostream>
#include <vector>
#include <algorithm>
struct S
{
S(int n) : value(n), isvalid(true)
{
}
S(const S& s) : value(s.value), isvalid(true)
{
}
S(S&& s) : value(s.value), isvalid(true)
{
s.isvalid = false;
}
S& operator =(S&& s)
{
value = s.value;
isvalid = s.isvalid;
s.isvalid = false;
return *this;
}
int value;
bool isvalid;
};
std::ostream& operator <<(std::ostream& outp, const S& s)
{
outp << s.value << '(' << std::boolalpha << s.isvalid << ')';
return outp;
}
int main()
{
std::vector<S> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x.value == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
Output
1(true) 3(true) 5(true)
1(true) 4(true) 5(false)
The bottom line: your references are still referring to the same slots they were before, but the elements have been either (a) move assigned to something else, or (a) no longer containing specified content. Tread carefully when using references to container contents when performing container modifications.
I reserve comment on the std::erase calls, as I have no idea what you're doing there at all. To my knowledge that isn't even a function in the standard library (wouldn't be the first time I missed out on a new function, but scraping over cppreference yields nothing, so take that for what its worth).