I know the following code does not work, and I fully understand why. What I actually do not understand is why not:
int main(int argc, char *argv[]) {
std::cout << (atoi(argv[1]) ? "foo" : 'b') << std::end;
}
Why: Of course, this expression may generate either a string or an integer, and this is the error pointed by the compiler:
error: operands to ?: have different types ‘const char*’ and ‘char’
Why not: Since the operator<< have a bind with both of the types const char* and char, why is it the compiler don't perform a code expansion as in a template -- which, I guess, is what is performed.
For example, if I had:
template <class T>
void a(const T& value) {
std::cout << a << std::endl;
}
I could call either a("foo") and a('b'), and -- I guess -- the compiler would do one expansion of the function with the type name [T = const char*] and another one with [T = char].
This may be a simple matter of what C++ does -- and what it does not --, but I fail to see if there's any corner case that would come up as an error if the expansion was performed.
C++ is a compiled statically-typed language and the type of an expression must be known at compile-time. The expression atoi(argv[1]) ? "foo" : 'b' could be a const char* or char, depending on the value of argv[1], which can't be known at compile-time. It's only when the program is actually executed that this value is known. So when the compiler is attempting to turn this expression into machine code, it can't decide which type to treat the expression as.
To see that it really doesn't have anything to do with the operator<<, just have the expression by itself:
int main(int argc, const char* argv[])
{
atoi(argv[1]) ? "foo" : 'b';
}
Even this won't compile, giving the following error:
error: operands to ?: have different types ‘const char*’ and ‘char’
It has nothing to do with cout or operator <<. The expression
atoi(argv[1]) ? "foo" : 'b'
itself wouldn't compile. The 2nd and 3rd operators that you feed to ?: must be either the same type, or types that are implicitly convertible to one other.
This is what you think you should be asking for:
#include <iostream>
#include <utility>
#include <type_traits>
#include <functional>
template<typename Left, typename Right>
auto tri( bool b, Left&& left, Right&& right )
-> decltype( std::forward<Left>(left)() )
{
if (b)
return std::forward<Left>(left)();
else
return std::forward<Right>(right)();
}
int main(int /*argc*/, char *argv[]) {
tri(
atoi(argv[1]),
[]()->std::ostream&{ return std::cout<<"foo"; },
[]()->std::ostream&{ return std::cout<<'b'; }
) << std::endl;
}
but it isn't what ? does.
C++ could be modified to do what you are asking, but the type cascade would grow boundlessly. Each time you have an expression that could return type A or type B, the calling code would have to be forked, which could cause further forking.
Signatures of functions would have to be expanded to list all of the types it "could" return.
Now, while this may be a worthwhile feature for C++ in the future, it isn't what C++ does now. Each expression in C++ has a single, definite type -- in template code, this occurs when you have instantiated the template.
As an aside, the ability to have poly-type return values in C++ would give you capabilities similar to exception handling, where a function could return a value or an error flag. As the calling code would have to automatically fork whenever you call a poly-type return value function, it would have to handle that error flag (either by returning it as an alternative type, or by handling it locally).
Related
#include <iostream>
enum mode { MODE0=0, MODE1, NUM_MODES};
int main(int args, char ** argv) {
int i = 1;
std::cout << (i == MODE0 ? "true" : "false") << "\n";
std::cout << (i == MODE1 ? "true" : "false") << "\n";
mode test;
test = i; // error
}
Why is it that the comparison of i to enum values works fine, but I get compilation error when assigning mode test variable to an integer value?
enum.cc:10:8: error: invalid conversion from 'int' to 'mode'
[-fpermissive]
My question is specifically about why comparison works and assignment doesn't (not how to fix my code) and it has received a couple of good explanations below.
MODE0, MODE1 and NUM_MODES are guaranteed to be convertible to int (the underlying type of the enum) but the reverse is not true. Not all int can be converted to mode. For example, what is the matching mode for the int 42? Simply put, only the implicit conversion from enum to int is defined, the opposite implicit conversion is not defined.
If you want to convert from int to mode you can preform a static_cast to signal that you are taking the responsibility of ensuring that the value being converted is always legal to convert to mode. Try
test = static_cast<mode>(i);
You can use strongly typed enumerations by adding the class keyword to your enum to prevent any implicit casts and to limit the scope of the enum value names. The definition would look like enum class mode { MODE0 = 0, MODE1, NUM_MODES };. In this case, you must quality the enum value names, for example, you would need to use mode::MODE0 instead of MODE0. This has the advantage that it avoids name collisions.
It's because there is a conversion from an enumerated type to int but there is no conversion in the opposite direction. For the comparison, MODE0 gets promoted to int. For the assignment, i would have to be converted to mode.
Look at it this way. When you do a comparison it doesn't matter if the int is not a valid possible value. If it is not then the comparison will fail and we can go on. Now when we go and try to assign an int to an enum you could assign to it a value that isn't mapped to the enum values. Since we don't want this implicitly happening the conversion is invalid. If you want to tell the compiler that it is okay, you know what you are doing, then you can cast it like:
test = static_cast<mode>(i);
Can someone explain me what is going on here...?
I had this code:
#include <fstream>
#include <string>
#include <iostream>
int main(){
std::ifstream file("test.txt");
std::string x;
while (true) {
if (!(file >> x)) return 0;
std::cout << x << "\n";
}
}
...compiles fine, does what it is supposed to do, no problem so far. Sometimes I dont like the ! so much, because it can be overlooked easily, so I replaced the if with
if ((file >> x)==false) return 0;
..and suddenly my compiler (gcc 4.8.5) complains with a warning:
warning: converting ‘false’ to pointer type ‘void*’ [-Wconversion-null]
if ((file >> x)==false) return 0;
and this is where I am starting to be puzzled. Where is the void* coming from? Doesnt >> return a reference that should be casted to a bool? Why is false converted to void*? Why isnt the same warning triggered when I dont explicitly write false?
Out of curiosity I also tried this:
if ((file>>x)==true) return 0;
which causes a storm of errors starting with
error: no match for ‘operator==’ (operand types are ‘std::basic_istream<char>’ and ‘bool’)
if ((file>>x)==true) return 0;
^
and now I am completely lost. How is false a differnt bool than true? Different values of course, but I always thought true and false are of same type.
Recall that C++ has operator overloads. In particular, std::basic_istream overloads operator!.
Alas, there is no enforcement that operator overloads are semantically consistent, so there is no overload for == between an istream and a bool. Thus the comparison with true fails. However, the compiler is also allowed to apply implicit conversions in order to make an expression compile - in this case false may be implicitly converted to a null pointer, and basic_istream has an overload of operator void* (though apparently that was replaced with operator bool in C++11 - presuambly to fix the inconsistency).
Here is my code:
#include <iostream>
#include "Generator.h" // user-defined class
char getChar(Generator & generator)
{
return generator.generateChar();
}
char getChar(int pos, const string & s)
{
return s[pos];
}
template<typename... StringType>
void func(Generator & generator, StringType &&... str)
{
char ch;
int size = sizeof...(StringType);
// lots of things to do
if (size == 0)
{
ch = getChar(generator);
}
else
{
ch = getChar(1, std::forward<StringType>(str)...); // ERROR here
}
}
int main(int argc, char ** argv)
{
Generator generator;
func(generator);
func(generator, "abc");
return 0;
}
At the beginning I just overloaded the function func and I found there were many similar codes. So I'm considering using the variadic template to get a better design. (How to make a better design if two overload functions are similar)
However I don't know why there is an error:
main.cpp:27:8: error: no matching function for call to 'getChar'
ch = getChar(1, std::forward(str)...);
main.cpp:37:2: note: in instantiation of function template specialization 'func<>' requested here
func(generator);
main.cpp:6:6: note: candidate function not viable: no known conversion from 'int' to 'Generator &' for 1st argument char
getChar(Generator & generator)
main.cpp:11:6: note: candidate function not viable: requires 2 arguments, but 1 was provided char getChar(int pos, const string & s)
By the way, can I have some design to avoid using if...else... working with sizeof...(StringType)?
When a template gets expanded, the entire template code gets expanded, and compiled, in its entirety.
Let's see what happens here:
func(generator);
In the resulting template-generated function, size will be 0, and the resulting function becomes:
if (0 == 0)
{
ch = getChar(generator);
}
else
{
ch = getChar(1);
}
Your compilation error becomes very obvious: getchar(1); does not match any overloaded instance of getChar(). The fact that the if statement is going to always evaluate to true, and the else part will never be executed doesn't matter. The else part must still be valid C++ code, that gets compiled, and it gets optimized away (maybe) only after it is compiled. And it can't be compiled, hence the compilation error.
Now that answers your question "I don't know why there is an error". Now you know. How to fix this becomes a different question, with the answer being, depending on the exact situation, some combination of template specialization, and/or SFINAE.
It looks like the example in your question is an abbreviated example (since the template function will never work, obviously, if the parameter pack has two or more parameters). That's fine (and is 100% compliant with the spirit of showing a minimum, complete, verifiable example), but coming up with an alternative compilable version of the shown code would probably not answer your real question.
I have a vector defined as:
std::vector<std::shared_ptr<Orders> > vOrders;
When adding to vOrders I want to add like:
vOrders.push_back(<std::shared_ptr<Orders> >([]() {
TimerForProcessingOrders _timerForProcessingOrders;
_timerForProcessingOrders.detach();
}));
but the compiler is giving me an error:
Expected expression: Expected '(' for function-style cast or type construction
It looks weird the < and > around std::shared_ptrbut removing it gives an error:
No matching conversion for functional-style cast from '<lambda >' to 'std::shared_ptr<Orders>'
What mistake am I making?
What this code does (in an ill formed way) is take the object constructed by the lambda, and store the lambda itself into the vector -- not an object of type shared_ptr<Order> which is what the vector requires.
but the compiler is giving me an error:
This is because the extra < > are completely invalid syntax, so the best the compiler can give you is "invalid expression".
It looks weird the < and > around std::shared_ptrbut removing it gives an error:
No matching conversion for functional-style cast from '<lambda >' to 'std::shared_ptr<Orders>'
Basically what you have here is shared_ptr<Orders>(/* .. Lambda .. */) -- and the compiler is saying that it has no idea how to turn the lambda into an Orders* to go into the shared_ptr.
What are you actually trying to accomplish here?
If you just want to transfer a pointer into the shared_ptr you can do something like
TimerForProcessingOrders _timerForProcessingOrders;
std::shared_ptr<Orders> ptr(_timerForProcessingOrders.detach());
vOrders.emplace_back(std::move(ptr));
no lambda is required.
What type is Orders? Actually, it doesn't really matter: the expression [](){ ... } certainly isn't of a type convertible to Orders*. ... and the angle brackets around the argument to push_back() a definitely wrong.
Did you mean to call the lambda expression?
vOrders.push_back(std::shared_ptr<Orders>([]() {
TimerForProcessingOrders _timerForProcessingOrders;
return _timerForProcessingOrders.detach();
})()
); // ^^ note these extra parenthesis
... but even that shouldn't work as the lambda expression doesn't return anything (hence the also added return).
Firstly, I'd suggest you follow Billy ONeals train of thought. But if you want to do it your way this is a SSCE of it.
There are two changes, this lambda returns an object and is executed, thereby returning.
#include <vector>
#include <memory>
#include <iostream>
struct Foo {
int val;
};
int main(int argc, char** argv) {
std::vector<std::shared_ptr<Foo> > v;
v.push_back([]() -> std::shared_ptr<Foo> { Foo f; f.val = 5; return std::make_shared<Foo>(f); }());
std::cout <<v[0]->val <<std::endl;
return 0;
}
Let’s say I have a template function that infers the length of an array parameter.
template <size_t S>
void join(const char d[], const char *(&arr)[S]) { }
If I call it like this, all is well:
const char *messages[] = {
"OK",
"Not OK",
"File not found"
};
join("\n", messages);
But if I call it with an empty array, like this:
const char *messages[] = { };
join("\n", messages);
…it doesn’t compile (with clang 4.0):
targs.cpp:9:5: error: no matching function for call to 'join'
join("\n", messages);
^~~~
targs.cpp:4:6: note: candidate template ignored: substitution failure [with S = 0]
void join(const char d[], const char *(&arr)[S]) { }
^
1 error generated.
I’m guessing that it has something to do with C++ not liking zero-length arrays, but if the function is not a template and takes the length as a separate parameter, it doesn’t complain about me declaring messages as a zero-length array.
What’s up here, and is there a nice workaround?
My actual use case is defining the parameters an HTTP API endpoint takes and looks something like this:
const api_param_t params[] = {
{ API_TYPE_STRING, "foo" },
{ API_TYPE_UINT64, "bar" },
{ API_TYPE_STRING, "baz" }
}
const api_status_t status_codes[] = { … };
const api_middleware_t middleware[] = { … };
new api_endpoint("/foo", params, status_codes, middleware);
Most endpoints take at least one parameter but many take none. It looks like this is, indeed, an extension which both GCC and clang implement (but, looks like, not completely…). I can think of a few workarounds:
Overload the api_endpoint constructor to special case zero-length arguments (but I need 23 of them to cover each zero-length-able parameter), which the GCC/clang extension is OK with.
Don’t try to infer the array length, take it as a separate parameter (and continue to use zero-length arrays)
Use a higher-level data structure like a vector for these parameters
Use a magic value to indicate "empty"
…but if anyone has better ideas I’d love to hear ‘em
This code isn't legal in the first place:
const char *messages[] = { };
Here are the errors and warnings my compiler produces:
main.cpp:6:26: warning: zero size arrays are an extension [-Wzero-length-array]
const char *messages[] = { };
^
main.cpp:7:1: error: no matching function for call to 'join'
join("\n", messages);
^~~~
main:3:6: note: candidate template ignored: substitution failure [with S = 0]: zero-length arrays are not permitted in C++
void join(const char d[], const char *(&arr)[S]) { }
^ ~
1 warning and 1 error generated.
So zero length arrays aren't actually allowed at all. Your compiler appears to have an extension for zero length arrays which, however, does not cover this specific case. Extensions are like that sometimes, because less work goes into extensions to make them work consistently with the whole language.
A workaround will depend on why you want a zero length array and how you're using it elsewhere. One workaround might be using a single element array instead.
Here's a work around. Since the extension does not allow array sizes to be deduced as zero add an overload that does not require this deduction:
template <size_t S>
void join(const char d[], const char *(&arr)[S]) {
std::cout << "array length > 0\n";
}
void join(const char d[], const char *(&arr)[0]) {
std::cout << "extension, zero length array\n";
}
int main() {
const char *messages[] = {
"OK",
"Not OK",
"File not found"
};
join("\n", messages);
const char *messages2[] = { };
join("\n", messages2);
}
You should keep in mind that this is using an extension and is not portable code. You may prefer to write portable code in order to avoid being locked into any particular C++ implementation. You can see how much you rely on this extension by adding the flag -Wzero-length-array to your builds.