So, I've read a lot online for this error, but for some reason, I'm still getting it even after I have tried the suggested things. If anyone could help me understand this and point out what's wrong, that would be awesome.
char * s = strtok(text, ",");
string name = s;
printf("%s", name);
Given your example code the error you get is saying something like you cannot pass a non-POD object to an ellipses. This is because you are trying to pass a non-POD type to a variadic function, one that takes a variable number of arguments. In this case by calling printf which is declared something like the below
int printf ( const char * format, ... );
The ellipsis used as the last parameter allows you to pass 0 or more additional arguments to the function as you are doing in your code. The C++ standard does allow you to pass a non-POD type but compilers are not required to support it. This is covered in part by 5.2.2/7 of the standard.
Passing a potentially-evaluated argument of class type having a non-trivial copy constructor, a non-trivial move contructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
This means it is up to each compiler maker to decide if they want to support it and how it will behave. Apparently your compiler does not support this and even if it did I wouldn't recommend using it.
Related
Why is the code below accepted by g++?
#include <cstdio>
#include <string>
int main()
{
std::string str;
scanf("%s", str);
}
What sense does it make to pass a class object to scanf()? Does it get converted to anything that could be useful to another function with variadic arguments?
scanf comes from C. In C if you wanted to have variable number of arguments (like scanf needs) the only solution was variadic function. Variadic functions by design are not type safe, i.e. you can pass absolutely any type and a varargs function will happily accept them. It is a limitation of the C language. That doesn't mean that any type is valid. If an type other than what is actually expected is passed, then we are in the wonderful land of Undefined Behavior.
That being said, scanf is a standard function and what it can accept is known, so most compilers will do extra checks (not required by the standard) if you enable the right flags. See Neil's answer for that.
In C++ (since C++11) we have parameter packs which are type safe ...ish (oh, concepts cannot get sooner).
Enable some warnings. With -Wextra -Wall -pedantic, you will get:
a.cpp:7:10: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'std::__cxx11::string' {aka 'std::__cxx11::basic_string<char>'} [-Wformat=]
scanf("%s", str);
If you want that to be an error rather than a warning, add -Werror.
You have two distinct problems here, not just one:
The passing of a std::string through variadic arguments (which has undefined behaviour), and
The passing of a std::string to a function whose logical semantics expected a char* instead (which has undefined behaviour).
So, no, it doesn't make sense. But it's not a hard error. If you're asking why this has undefined behaviour rather than being ill-formed (and requiring a hard error), I do not know specifically but the answer is usually that it was deemed insufficiently important to require compilers to go to the trouble it would take to diagnose it.
Also, it would be unusual for a logical precondition violation to be deemed ill-formed (just as a matter of convention and consistency; many such violations could not be detected before runtime), so I'd expect point #2 to have undefined behaviour regardless of what hypothetical changes we made to the language to better reject cases of point #1.
Anyway, in the twenty years since standardisation, we've reached a point in technology where the mainstream toolchains do warn on it anyway, and since warnings can be turned into errors, it doesn't really matter.
To answer each of your questions...
The question in the title: "Why isn't it a compile error if you pass a class object to scanf?"
Because the declaration of scanf is int scanf ( const char * format, ... ); which means it will accept any number of arguments after the format string as variadic arguments. The rules for such arguments are:
When a variadic function is called, after lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions, each argument that is a part of the variable argument list undergoes additional conversions known as default argument promotions:
std::nullptr_t is converted to void*
float arguments are converted to double as in floating-point promotion
bool, char, short, and unscoped enumerations are converted to int or wider integer types as in integer promotion
Only arithmetic, enumeration, pointer, pointer to member, and class type arguments are allowed (except class types with non-trivial copy constructor, non-trivial move constructor, or a non-trivial destructor, which are conditionally-supported with implementation-defined semantics)
Since std::string is a class type with non-trivial copy and move constructors, passing the argument is not allowed. Interestingly, this prohibition, while checkable by a compiler, is not rejected by the compiler as an error.
The first question in the body: "Why is the code below accepted by g++?"
That is a great question. The other answer by #LightnessRacesInOrbit addresses this point very well.
Your second question in the body: "Does it get converted to anything that could be useful to another function with variadic arguments?"
If you run the code, one of the possible results (at run time) is:
.... line 5: 19689 Segmentation fault (core dumped)
so, no, it is not converted into anything, in general, at least not implicitly.
The clarifying question in the comment thread to the question: "I wanted to know "why does the C++ language not disallow this"".
This question appears to be a subjective one, touching on why the C++ language designer(s) and perhaps even the C language designers, did not make their language design robust enough for the language definition to prohibit something other than a string, or memory buffer, or any number of other things, to be sensible as a non-initial argument to scanf. What we do know is that a compiler can often determine such things (that's what linters do, after all!) but we can only guess, really. My guess is that in order to make scanf super typesafe (in the language definition, as opposed to needing a linter) they would need to redefine scanf to use template arguments of some sort. However scanf comes from C, so they did not want to change its signature (that would indeed be wrong, given that C++ wants to be a C superset...).
Starting out in c++ and noticed that you could initialise a variable in two ways
int example_var = 3; // with the assignment operator '='
or
int example_var(3); // enclosing the value with parentheses
is there a reason to use one over the other?
The first form dates back from the C time, while the second was added in C++. The reason for the addition is that in some contexts (in particular initializer lists in constructors) the first form is not allowed.
The two are not exactly equivalent for all types, and that is where one or the other might be more useful. The first form semantically implies the creation of a temporary from the right hand side, followed by the copy construction of the variable from that temporary. The second form, is direct initialization of the variable from the argument.
When does it matter?
The first form will fail if there is no implicit conversion from the right hand side to the type of the variable, or if the copy constructor is not available, so in those cases you will have to use direct initialization.
The second form can be used in more contexts than the first, but it is prone to the most-vexing-parse. That is, in some cases the syntax will become compatible with a declaration for a function (rather than the definition of a regular variable), and the language determines that when this is the case, the expression is to be parsed as a function declaration:
std::string s = std::string(); // ok declares a variable
std::string s( std::string() ); // declares a function: std::string s( std::string(*)() )
Finally in C++11 there is a third form, that uses curly braces:
std::string s{std::string{}};
This form has the advantages of direct initialization with parenthesis, but at the same time it is not prone to misinterpretation.
Which one to use?
I would recommend the third option if available. That being said, I tend to use the first more often than not, or the second depending on the context and the types...
For built in types like int both mean the same.
But for custom data types they can mean different. First format is called Copy Initialization while second is called Direct Initialization.
Good Read:
Is there a difference in C++ between copy initialization and direct initialization?
Their output is the same...
the both the syntax call the copy constructor.
It is same for int and other similar built in data types, though some difference is there for user defined data types.
They compile to the same thing. However, both are a form of variable initialization, not assignment, which matters a little in C and a lot in C++ since totally different functions (constructor v. assignment) are called.
I learned that nullptr, in addition to being convertible to any pointer type (but not to any integral type) also has its own type std::nullptr_t. So it is possible to have a method overload that accepts std::nullptr_t.
Exactly why is such an overload required?
If more than one overload accepts a pointer type, an overload for std::nullptr_t is necessary to accept a nullptr argument. Without the std::nullptr_t overload, it would be ambiguous which pointer overload should be selected when passed nullptr.
Example:
void f(int *intp)
{
// Passed an int pointer
}
void f(char *charp)
{
// Passed a char pointer
}
void f(std::nullptr_t nullp)
{
// Passed a null pointer
}
There are some special cases that comparison with a nullptr_t type is useful to indicate whether an object is valid.
For example, the operator== and operator!= overloads of std::function could only take nullptr_t as the parameter to tell if the function object is empty. For more details you could read this question.
Also, what other type would you give it, that doesn't simply re-introduce the problems we had with NULL? The whole point is to get rid of the nasty implicit conversions, but we can't actually change behaviour of old programs so here we are.
The type was introduced to avoid confusion between integer zero and the the null memory. And as always cpp gives you access to the type. Where as Java only gives you access to the value. It really doesnt matter what purpose you find for it. I normally use it as a token in function overloading.
But I have some issues with the implementation of cpp null const.
Why didnt they just continue with NULL or null? That definition was already being used for that purpose. What about code that already was using nullptr for something else.
Not to mention nullptr is just too long. Annoying to type and ugly to look at most times. 6 characters just to default initialize a variable.
With the introduction of nullptr, you would think zero would no longer be both a integer and null pointer const. However zero still holds that annoying ambiguity. So I dont see the sense then of this new nullptr value. If you define a function that can accept an integer or a char pointer, and pass zero to that function call, the compiler will complain that it is totally ambigious! And I dont think casting to an integer will help.
Finally, it sucks that nullptr_t is part of the std namespace and not simply a keyword. Infact I am just learning this fact, after how long I have been using nullptr_t in my functions. MinGW32 that comes with CodeBlocks allows you to get away with using nullptr_t with std namespace. Infact MinGW32 allows void* increment and a whole lot of other things.
Which leads me to: cpp has too much denominations and confusion. To the point where code compatibility with one compiler is not compatibility with another of the same cpp version. Static library of one compiler cannot work with a different compiler. There is no reason why it has to be this way. And I think this is just one way to help kill cpp.
This question is about vararg functions, and the last named parameter of them, before the ellipsis:
void f(Type paramN, ...) {
va_list ap;
va_start(ap, paramN);
va_end(ap);
}
I was reading in the C Standard, and found the following restriction for the va_start macro:
The parameter parmN is the identifier of the rightmost parameter in the variable parameter list in the function definition (the one just before the , ...). If the parameter parmN is declared with the register storage class, with a function or array type, or with a type that is not compatible with the type that results after application of the default argument promotions, the behavior is undefined.
I wonder why the behavior is undefined for the following code
void f(int paramN[], ...) {
va_list ap;
va_start(ap, paramN);
va_end(ap);
}
and not undefined for the following
void f(int *paramN, ...) {
va_list ap;
va_start(ap, paramN);
va_end(ap);
}
The macros are intended to be implementable by pure C code. But pure C code cannot find out whether or not paramN was declared as an array or as a pointer. In both cases, the type of the parameter is adjusted to be a pointer. The same is true for function type parameters.
I wonder: What is the rationale of this restriction? Do some compilers have problems with implementing this when these parameter adjustments are in place internally? (The same undefined behavior is stated for C++ - so my question is about C++ aswell).
The restriction against register parameters or function parameters are probably something like:
you are not allowed to take the address of a variable with the register storage class.
function pointers are sometimes quite different than pointers to objects. For example, they might be larger than pointers to objects (you can't reliably convert a function pointer to an object pointer and back again), so adding some fixed number to the address of a function pointer might not get you to the next parameter. If va_start() and/or va_arg() were implemented by adding some fixed amount to the address of paramN and function pointers were larger than object pointers the calculation would end up with the wrong address for the object va_arg() returns. This might not seem to be a great way to implement these macros, but there might be platforms that have (or even need) this type of implementation.
I can't think of what the problem would be to prevent allowing array parameters, but PJ Plauger says this in his book "The Standard C Library":
Some of the restrictions imposed on the macros defined in <stdarg.h> seem unnecessarily severe. For some implementations, they are. Each was introduced, however, to meet the needs of at least one serious C implementation.
And I imagine that there are few people who know more about the ins and outs of the C library than Plauger. I hope someone can answer this specific question with an actual example; I think it would be an interesting bit of trivia.
New info:
The "Rationale for International Standard - Programming Languages - C" says this about va_start():
The parmN argument to va_start was intended to be an aid to implementors writing the
definition of a conforming va_start macro entirely in C, even using pre-C89 compilers (for example, by taking the address of the parameter). The restrictions on the declaration of the parmN parameter follow from the intent to allow this kind of implementation, as applying the & operator to a parameter name might not produce the intended result if the parameter’s declaration did not meet these restrictions.
Not that that helps me with the restriction on array parameters.
It's not undefined. Keep in mind that when parameter is declared as int paramN[], the actual parameter type will still decay to int* paramN immediately (which is visible in C++, for example, if you apply typeid to paramN).
I must admit that I'm not sure what this bit in the spec is even for, considering that you cannot have parameters of function or array types in the first place (since they will pointer-decay).
I found another relevant quote, from Dinkumware.
The last parameter must not have
register storage class, and it must
have a type that is not changed by the
translator. It cannot have:
* an array type
* a function type
* type float
* any integer type that changes when promoted
* a reference type [C++ only]
So apparently, the problem is precisely that the parameter gets passed in a way different from how it is declared. Interestingly enough, they also ban float and short, even though those should be supported by the standard.
As a hypothesis, it could be that some compilers have problems doing sizeof correctly on such parameters. E.g. it might be that, for
int f(int x[10])
{
return sizeof(x);
}
some (buggy) compiler will return 10*sizeof(int), thus breaking the va_start implementation.
I can only guess that the register restriction is there to ease library/compiler implementation -- it eliminates a special case for them to worry about.
But I have no clue about the array/function restriction. If it were in the C++ standard only, I would hazard a guess that there is some obscure template matching scenario where the difference between a parameter of type T[] and of type T* makes a difference, correct handling of which would complicate va_start etc. But since this clause appears in the C standard too, obviously that explanation is ruled out.
My conclusion: an oversight in the standards. Possible scenario: some pre-standard C compiler implemented parameters of type T[] and T* differently, and the spokesperson for that compiler on the C standards committee had the above restrictions added to the standard; that compiler later became obsolete, but no-one felt the restrictions were compelling enough to update the standard.
C++11 says:
[n3290: 13.1/3]: [..] Parameter declarations that differ only in a
pointer * versus an array [] are equivalent. That is, the array
declaration is adjusted to become a pointer declaration. [..]
and C99 too:
[C99: 6.7.5.3/7]: A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. [..]
And you said:
But pure C code cannot find out whether or not paramN was declared as an array or as a pointer. In both cases, the type of the parameter is adjusted to be a pointer.
Right, so there's no difference between the two pieces of code you showed us. Both have paramN declared as a pointer; there is actually no array type there at all.
So why would there be a difference between the two when it comes to the UB?
The passage you quoted...
The parameter parmN is the identifier of the rightmost parameter in the variable parameter list in the function definition (the one just before the , ...). If the parameter parmN is declared with the register storage class, with a function or array type, or with a type that is not compatible with the type that results after application of the default argument promotions, the behavior is undefined.
...applies to neither, as would be expected.
I'm trying to fix something in some Objective C++ (?!) code. I don't know either of those languages, or any of the relevant APIs or the codebase, so I'm getting stymied left and right.
Say I have:
Vector<char, sizeof 'a'>& sourceData();
sourceData->append('f');
When i try to compile that, I get:
error: request for member 'append' in 'WebCore::sourceData', which is of non-class type 'WTF::Vector<char, 1ul >& ()();
In this case, Vector is WTF::Vector (from WebKit or KDE or something), not STD::Vector. append() very much is supposed to be a member of class generated from this template, as seen in this documentation. It's a Vector. It takes the type the template is templated on.
Now, because I never write programs in Real Man's programming languages, I'm hella confused about the notations for references and pointers and dereferences and where we need them.
I ultimately want a Vector reference, because I want to pass it to another function with the signature:
void foobar(const Vector<char>& in, Vector<char>& out)
I'm guessing the const in the foobar() sig is something I can ignore, meaning 'dont worry, this won't be mangled if you pass it in here'.
I've also tried using .append rather than -> because isn't one of the things of C++ references that you can treat them more like they aren't pointers? Either way, its the same error.
I can't quite follow the error message: it makes it sound like sourceData is of type WTF:Vector<char, 1ul>&, which is what I want. It also looks from the those docs of WTF::Vector that when you make a Vector of something, you get an .append(). But I'm not familiar with templates, either, so I can't really tell i I'm reading that right.
EDIT:
(This is a long followup to Pavel Minaev)
WOW THANKS PROBLEM SOLVED!
I was actually just writing an edit to this post that I semi-figured out your first point after coming across a reference on the web that that line tells the compiler your forward declaring a func called sourceData() that takes no params and returns a Vector of chars. so a "non-class type" in this case means a type that is not an instance of a class. I interpreted that as meaning that the type was not a 'klass', i.e. the type of thing you would expect you could call like .addMethod(functionPointer).
Thanks though! Doing what you suggest makes this work I think. Somehow, I'd gotten it into my head (idk from where) that because the func sig was vector&, I needed to declare those as &'s. Like a stack vs. heap pass issue.
Anyway, that was my REAL problem, because I tried what you'd suggested about but that doesn't initialize the reference. You need to explicitly call the constructor, but then when I put anything in the constructor's args to disambiguate from being a forward decl, it failed with some other error about 'temporary's.
So in a sense, I still don't understand what is going on here fully, but I thank you heartily for fixing my problem. if anyone wants to supply some additional elucidation for the benefit of me and future google people, that would be great.
This:
Vector<char, sizeof 'a'>& sourceData();
has declared a global function which takes no arguments and returns a reference to Vector. The name sourceData is therefore of function type. When you try to access a member of that, it rightfully complains that it's not a class/struct/union, and operator-> is simply inapplicable.
To create an object instead, you should omit the parentheses (they are only required when you have any arguments to pass to the constructor, and must be omitted if there are none):
Vector<char, sizeof 'a'> sourceData;
Then you can call append:
sourceData.append('f');
Note that dot is used rather than -> because you have an object, not a pointer to object.
You do not need to do anything special to pass sourceData to a function that wants a Vector&. Just pass the variable - it will be passed by reference automatically:
foobar(sourceData, targetData);
Dipping your toes in C++ is never much fun. In this case, you've run into a couple of classic mistakes. First, you want to create an instance of Vector on the stack. In this case the empty () is interpreted instead as a declaratiton of a function called sourceData that takes no agruments and returns a reference to a Vector. The compiler is complaining that the resulting function is not a class (it's not). To create an instance of Vector instead, declare the instance without the () and remove the &. The parentheses are only required if you are passing arguments to the instance constructor and must be omitted if there are no arguments.
You want
Vector<char, sizeof 'a'> sourceData;
sourceData.append('f');
Vector<char, sizeof 'a'> outData; //if outData is not instantiated already
foobar(sourceData, outData);
This Wikipedia article gives a decent introduction to C++ references.