This form does not compile with my VS2008 compiler. Should it be possible?
#include <iostream>
using namespace std;
int getvalue() { return 3; }
int main(int argc, char* argv[])
{
if((int val = getvalue()) == 3)
cout << "val=" << val << "\n";
return 0;
}
This form does work.
...
int val;
if((val = getvalue()) == 3)
...
Why does it not work?
It's not legal because you can't use a statement as an expression.
So, it's not the declaring a variable inside an if that's illegal, but the comparison.
Just like:
(int x = 3) == 3;
is illegal, whereas
int x = 3;
x == 3;
isn't.
If you don't want to litter in your scope, you can use а {} block:
...
{
int val;
if((val = getvalue()) == 3) {
...
}
}
...
val will be destroyed at the last } and won't be visible afterwards.
Related
Let's say I have this as current code:
if (GetFlag(...)) {
if (auto x = GetX(...)) {
if (auto y = GetY(...)) {
...
}
}
}
and that the method calls and variable definitions need to be made in this order for both correctness and performance reasons (we don't want to calculate x if GetFlag(...) returns false, and we don't want to calculate y if x is nullptr).
Is there a way to refactor this into a "one-liner"? The closest I got to actually compiling would be
if (auto x = GetX(...); auto x = GetX(...) && GetFlag(...)) { ... }
but this does not preserve the order of operations.
Thanks!
You can extract the conditionals into a separate function that returns a tuple, and structured bind in the if
std::tuple<bool, std::optional<X>, std::optional<Y>> getFlagsXY()
{
if (!getFlags())
{
return { false };
}
if (auto x = getX(); !x)
{
return { true, std::move(x) };
}
else
{
return { true, std::move(x), getY() };
}
}
if (auto [f, x, y] = getFlagsXY(); f && *x && *y) { ... }
And yes it is possible.
If statements stop processing terms once they see which branch is going to be taken. Try changing the values of GetFlag, GetX to see when the body of the if gets called.
#include <iostream>
bool GetFlag()
{
std::cout << "GetFlag\n";
return true;
}
int GetX()
{
std::cout << "GetX\n";
return 0;
}
int GetY()
{
std::cout << "GetY\n";
return 3;
}
int main()
{
int x;
int y;
if (GetFlag() && (x = GetX()) && (y = GetY()))
{
std::cout << "Do " << x << ", " << y;
}
return 0;
}
My Code is below.
struct conv{
struct des
{
des(int a) {}
des(int a, int b) {}
des(int a, int b, int c) {}
};
};
int main(int argc, const char * argv[]) {
int a = 1;
int b = 1;
int c = 1;
if (a > 1)
{
auto conv_desc = conv::des(1, 2, 3);
} else if(b > 1) {
auto conv_desc = conv::des(1, 2);
} else {
auto conv_desc = conv::des(1);
}
return 0;
}
The code's pattern is extracted from mkldnn. The only thing I want to do is take auto conv_desc out of the if-else statement.
I tried to declare auto conv_desc out of the if-else statement.
It occurred an error:
Declaration of
variable 'conv_desc' with deduced type 'auto' requires an initializer
Or if I used the pointer like below, I got a null pointer.
Another way got an error:
Taking the address of a temporary object of type 'conv::des'
If I can't solve this problem, I will have to write a large piece of duplicate code in each branch.
Move your if code into separate function:
conv::des make_des(int a, int b) {
if (a > 1) {
return conv::des(1, 2, 3);
} else if(b > 1) {
return conv::des(1, 2);
} else {
return conv::des(1);
}
}
int main(int argc, const char * argv[]) {
int a = 1;
int b = 1;
int c = 1;
auto conv_desc = make_des(a, b);
return 0;
}
Don't use auto. If you need to declare a variable and can't assign a value yet, then auto can't be used.
int main(int argc, const char * argv[]) {
// ...
conv::des conv_desc; // calls default constructor.
if (a > 1)
{
conv_desc = conv::des(1, 2, 3);
} else if(b > 1) {
conv_desc = conv::des(1, 2);
} else {
conv_desc = conv::des(1);
}
// conv_desc is initialized at this point.
return 0;
}
You can't use a pointer like this
int *a;
{ // new scope
int b = 5;
a = &b; // error.
}
// b is no longer in scope here
When b goes out of scope, then a will be pointing at the address where b used to be, which is now no longer valid.
If you want to use a pointer then you can use new. However in that case you must release the memory afterwards.
int main(int argc, const char** argv)
{
conv::des *conv_desc = nullptr; // does not call default constructor.
if (a > 1)
{
conv_desc = new conv::des(1, 2, 3);
} else if(b > 1) {
conv_desc = new conv::des(1, 2);
} else {
conv_desc = new conv::des(1);
}
if (conv_desc == nullptr) { /* memory allocation failed */ }
// conv_desc is initialized at this point.
// ...
// remember to delete conv_desc
if (conv_desc != nullptr) { delete conv_desc; conv_desc = nullptr; }
return 0;
}
You also can use an immediately called lambda. This is a common pattern to make some variables const when they could not otherwise.
This solution is very similar to #yachoor answer but uses a lambda.
int main(int argc, const char * argv[]) {
int a = 1;
int b = 1;
int c = 1;
// Kind of an inline function that is called immediately
auto const conv_desc = [&]{
if (a > 1) {
return conv::des(1, 2, 3);
} else if(b > 1) {
return conv::des(1, 2);
} else {
return conv::des(1);
}
}();
// Parens calls the function
return 0;
}
Seems like you are making the code overly-complicated. It is not clear why you would want to differentiate between these three constructors in this way. Why not hide the complexity inside the object by using default values like this?
struct conv{
struct des
{
des(int a, int b = 0, int c = 0) {
if(a > 1) {
/// do some logic
} else if(b > 1) {
// do some logic
} else {
// do some logic
}
}
};
};
int main(int argc, const char * argv[]) {
int a = 1;
int b = 1;
int c = 1;
auto conv_desc = conv::des(a, b, c);
return 0;
}
If you move the if ... else chain into a separate function, you can use return
struct conv
{
struct des
{
des(int a) {
}
des(int a, int b) {
}
des(int a, int b, int c) {
}
};
};
conv::des make_conv_des(int a, int b)
{
if (a > 1) {
return conv::des(1, 2, 3);
} else if(b > 1) {
return conv::des(1, 2);
} else {
return conv::des(1);
}
}
int main(int argc, const char * argv[]) {
int a = 1;
int b = 1;
auto conv_des = make_conv_des(a, b);
return 0;
}
I have a code that uses a while statement. Inside
the conditional expression I do a lookup. At the same time the conditional expression checks the return value:
#include <map>
struct a {
a *up;
std::map<int,int> tbl;
};
void p(a *_a, int id) {
decltype(_a->tbl)::iterator i;
while (_a && (i = _a->tbl.find(id)) != _a->tbl.end()) {
i->second += 1;
_a = _a->up;
}
}
int main(int arc, char **argv) {
a _a1{0,{{0,10},{1,10}}};
a _a0{&_a1,{{2,11},{3,11}}};
p(&_a0, 0);
return 0;
}
However I would like to get rid of the explicit declaration of i. I would like to use auto. In pseudocode:
...
void p(a *_a, int id) {
while (_a && ((auto i = _a->tbl.find(id))) != _a->tbl.end()) {
i->second += 1;
_a = _a->up;
}
}
...
Is there a construct in c++11/14/17 that supports this kind of declarations inside an expression? Not only while(auto i = 1) {...} style declarations? Maybe there are some new features that allow this?
How about separating the two conditions? The main one for the loop to continue is _a, and the secondary one (which may always be true) is _a->tbl.find(id) != _a->tbl.end(). Return on its negation:
void p(a *_a, int id) {
while (_a) {
auto i = _a->tbl.find(id);
if (i == _a->tbl.end()) {
return;
}
i->second += 1;
_a = _a->up;
}
}
In a C++14 program, I am given a string like
std::string s = "MyFile####.mp4";
and an integer 0 to a few hundred. (It'll never be a thousand or more, but four digits just in case.) I want to replace the "####" with the integer value, with leading zeros as needed to match the number of '#' characters. What is the slick C++11/14 way to modify s or produce a new string like that?
Normally I would use char* strings and snprintf(), strchr() to find the "#", but figure I should get with modern times and use std::string more often, but know only the simplest uses of it.
What is the slick C++11/14 way to modify s or produce a new string like that?
I don't know if it's slick enough but I propose the use of std::transform(), a lambda function and reverse iterators.
Something like
#include <string>
#include <iostream>
#include <algorithm>
int main ()
{
std::string str { "MyFile####.mp4" };
int num { 742 };
std::transform(str.rbegin(), str.rend(), str.rbegin(),
[&](auto ch)
{
if ( '#' == ch )
{
ch = "0123456789"[num % 10]; // or '0' + num % 10;
num /= 10;
}
return ch;
} // end of lambda function passed in as a parameter
); // end of std::transform()
std::cout << str << std::endl; // print MyFile0742.mp4
}
I would use regex since you're using C++14:
#include <iostream>
#include <regex>
#include <string>
#include <iterator>
int main()
{
std::string text = "Myfile####.mp4";
std::regex re("####");
int num = 252;
//convert int to string and add appropriate number of 0's
std::string nu = std::to_string(num);
while(nu.length() < 4) {
nu = "0" + nu;
}
//let regex_replace do it's work
std::regex_replace(std::ostreambuf_iterator<char>(std::cout),
text.begin(), text.end(), re, nu);
std::cout << std::endl;
return 0;
}
WHy not use std::stringstream and than convert it to string.
std::string inputNumber (std::string s, int n) {
std::stringstream sstream;
bool numberIsSet = false;
for (int i = 0; i < s; ++i) {
if (s[i] == '#' && numberIsSet == true)
continue;
else if (s[i] == '#' && numberIsSet == false) {
sstream << setfill('0') << setw(5) << n;
numberIsSet = true;
} else
sstream << s[i];
}
return sstream.str();
}
I would probably use something like this
#include <iostream>
using namespace std;
int main()
{
int SomeNumber = 42;
std:string num = std::to_string(SomeNumber);
string padding = "";
while(padding.length()+num.length()<4){
padding += "0";
}
string result = "MyFile"+padding+num+".mp4";
cout << result << endl;
return 0;
}
Mine got out of control while I was playing with it, heh.
Pass it patterns on its command line, like:
./cpp-string-fill file########.jpg '####' test###this### and#this
#include <string>
#include <iostream>
#include <sstream>
std::string fill_pattern(std::string p, int num) {
size_t start_i, end_i;
for(
start_i = p.find_first_of('#'), end_i = start_i;
end_i < p.length() && p[end_i] == '#';
++end_i
) {
// Nothing special here.
}
if(end_i <= p.length()) {
std::ostringstream os;
os << num;
const std::string &ns = os.str();
size_t n_i = ns.length();
while(end_i > start_i && n_i > 0) {
end_i--;
n_i--;
p[end_i] = ns[n_i];
}
while(end_i > start_i) {
end_i--;
p[end_i] = '0';
}
}
return p;
}
int main(int argc, char *argv[]) {
if(argc<2) {
exit(1);
}
for(int i = 1; i < argc; i++) {
std::cout << fill_pattern(argv[i], 1283) << std::endl;
}
return 0;
}
I would probably do something like this:
using namespace std;
#include <iostream>
#include <string>
int main()
{
int SomeNumber = 42;
string num = std::to_string(SomeNumber);
string guide = "myfile####.mp3";
int start = static_cast<int>(guide.find_first_of("#"));
int end = static_cast<int>(guide.find_last_of("#"));
int used = 1;
int place = end;
char padding = '0';
while(place >= start){
if(used>num.length()){
guide.begin()[place]=padding;
}else{
guide.begin()[place]=num[num.length()-used];
}
place--;
used++;
}
cout << guide << endl;
return 0;
}
Let's imagine I have piece of code like this:
#include <iostream>
int main()
{
int a = 5;
{
int a = 12;
std::cout << a;
}
return 0;
}
I want to cout a==5 from outside scope, but main::a doesn't work surely. Is there any workaround?
A (let's say) workaround:
int main()
{
int a = 5;
int *pa = &a;
{
int a = 12;
std::cout << (*pa);
}
return 0;
}
Alternatively,
int main()
{
int a = 5;
int& ra = a;
{
int a = 12;
std::cout << ra;
}
return 0;
}
An alternative, it's similar to ilya answer but without polluting the parent scope
int main() {
int a = 1;
{
int& outer_a = a;
int a = 2;
std::cout << outer_a;
}
}