Absence of compilation error when using parametrized constructor - c++

Today at work I came across a behavior in C++ which I don't understand. I have produced the following example code to illustrate my problem:
#include <string>
#include <iostream>
class MyException
{
public:
MyException(std::string s1) {std::cout << "MyException constructor, s1: " << s1 << std::endl;}
};
int main(){
const char * text = "exception text";
std::cout << "Creating MyException object using std::string(const char *)." << std::endl;
MyException my_ex(std::string(text));
std::cout << "MyException object created." << std::endl;
//throw my_ex;
std::string string_text("exception text");
std::cout << "Creating MyException object using std::string." << std::endl;
MyException my_ex2(string_text);
std::cout << "MyException object created." << std::endl;
// throw my_ex2;
return 0;
}
This code snippet compiles without any errors and produces the following output:
$ g++ main.cpp
$ ./a.out
Creating MyException object using std::string(const char *).
MyException object created.
Creating MyException object using std::string.
MyException constructor, s1: exception text
MyException object created.
Note that for my_ex the constructor I have defined was not called. Next, if I want to actually throw this variable:
throw my_ex;
I get a compilation error:
$ g++ main.cpp
/tmp/ccpWitl8.o: In function `main':
main.cpp:(.text+0x55): undefined reference to `my_ex(std::string)'
collect2: error: ld returned 1 exit status
If I add braces around the conversion, like this:
const char * text = "exception text";
std::cout << "Creating MyException object using std::string(const char *)." << std::endl;
MyException my_ex((std::string(text)));
std::cout << "MyException object created." << std::endl;
throw my_ex;
Then it works as I would have expected:
$ g++ main.cpp
$ ./a.out
Creating MyException object using std::string(const char *).
MyException constructor, s1: exception text
MyException object created.
terminate called after throwing an instance of 'MyException'
Aborted (core dumped)
I have the following questions:
Why does my first example compile? How come I don't get a compilation error?
Why doesn't the code compile, when I try to throw my_ex;?
Why do the braces resolve the problem?

According to most vexing parse, MyException my_ex(std::string(text)); is a function declaration; the function is named my_ex, taking a parameter named text with type std::string, returns MyException. It's not an object definition at all, then no constructor will be called.
Note the error message undefined reference to 'my_ex(std::string)' for throw my_ex; (you're trying to throw a function pointer in fact), which means that can't find the definition of the function my_ex.
To fix it you can add additional parentheses (as you has shown) or use braces which supported from C++11:
MyException my_ex1((std::string(text)));
MyException my_ex2{std::string(text)};
MyException my_ex3{std::string{text}};

The answer is to use {} (braced-init) as much as possible. Sometimes, though, it might be missed inadvertently. Luckily, compilers (like clang, under no extra warning flags) can hint:
warning: parentheses were disambiguated as a function declaration [-Wvexing-parse]
MyException my_ex(std::string(text));
^~~~~~~~~~~~~~~~~~~
test.cpp:13:23: note: add a pair of parentheses to declare a variable
MyException my_ex(std::string(text));
^
( )
1 warning generated.
which will immediately point you the issue.

Related

Must `throw nullptr` be caught as a pointer, regardless of pointer type?

The following program throws nullptr and then catches the exception as int*:
#include <iostream>
int main() {
try {
throw nullptr;
}
catch(int*) {
std::cout << "caught int*";
}
catch(...) {
std::cout << "caught other";
}
}
In Clang and GCC the program successfully prints caught int*, demo: https://gcc.godbolt.org/z/789639qbb
However in Visual Studio 16.11.2 the program prints caught other. Is it a bug in MSVC?
Looks like a bug in Visual Studio, according to the standard [except.handle]:
A handler is a match for an exception object of type E if
[...]
the handler is of type cv T or const T& where T is a pointer or pointer-to->member type and E is std​::​nullptr_t.

In which context default argument of immediate function is substituted in C++20 (on source location example)?

In C++20 a new feature was added to get the source location information: https://en.cppreference.com/w/cpp/utility/source_location
Here is a slightly modified example from that page, where an addition immediate function loc is used to get the source location:
#include <iostream>
#include <string_view>
#include <source_location>
consteval auto loc(std::source_location x = std::source_location::current() ) { return x; }
void log(const std::string_view message,
const std::source_location location = loc()) {
std::cout << "file: "
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ") `"
<< location.function_name() << "`: "
<< message << '\n';
}
template <typename T> void fun(T x) { log(x); }
int main(int, char*[]) {
log("Hello world!");
fun("Hello C++20!");
}
In the latest MSVC 2019 it prints as in the original example from cppreference.com:
file: main.cpp(25:5) `main`: Hello world!
file: main.cpp(20:5) `fun`: Hello C++20!
But in GCC the same line is indicated twice in the output:
file: /app/example.cpp(8:51) ``: Hello world!
file: /app/example.cpp(8:51) ``: Hello C++20!
demo: https://gcc.godbolt.org/z/nqE4cr9d4
Which of the compilers is right here?
And if define loc function not as immediate one:
auto loc(std::source_location x = std::source_location::current() ) { return x; }
then the output of GCC changes and resembles the original example:
file: /app/example.cpp(20:8) `int main(int, char**)`: Hello world!
file: /app/example.cpp(17:42) `void fun(T) [with T = const char*]`: Hello C++20!
While MSVC refuses to compile it with the error:
error C7595: 'std::source_location::current': call to immediate function is not a constant expression
demo: https://gcc.godbolt.org/z/vorW4f9ax
Please also suggest, which compiler is right in not-immediate case as well?
This is explained in 17.8.2.1.2 [support.srcloc.class] (emphasis mine):
Remarks: Any call to current that appears as a default member initializer (11.4), or as a subexpression
thereof, should correspond to the location of the constructor definition or aggregate initialization that
uses the default member initializer. Any call to current that appears as a default argument (9.3.3.6),
or as a subexpression thereof, should correspond to the location of the invocation of the function that
uses the default argument (7.6.1.2).
From this, I deduce that GCC is right.
When the call to current happens in line 5, it returns a source_location object that "correspond[s] to the location of the invocation of the function (in this case the function loc) that
uses the default argument".
In this case the invocation location is 8:51 (the expression const std::source_location location = loc()).
Why the function name is empty is explained by the following:
17.8.2.1.1.1 (Table 38) tells us that the function name should be "such as in" __func__.
Element
Value
function_name_
A name of the current function such as in __func__ (9.5.1) if any, an empty string otherwise.
9.5.1.8 Example shows that if __func__ appears as a default argument, the name is undefined. I know that examples are nonnormative text, but this clearly describes the intent:
[Example:
struct S {
S() : s(__func__) { } // OK
const char* s;
};
void f(const char* s = __func__); // error: __func__ is undeclared
— end example]

Programmer-Defined Exception Class problem

I am trying to implement the code sample from a text book. It is trying to create an inherited Exception class from exception with code below.
#include <stdexcept>
#include <string>
using namespace std;
class TargetNotFoundException: public exception {
public :
TargetNotFoundException(const string& message = "")
: exception("Target not found: " + message.c_str()){ } // end constructor
}; // end TargetNotFoundException
The constructor provides a way for a throw statement to identify the condition that caused the exception. For example, the statement
throw TargetNotFoundException (target + " not found in a box!");
invokes the constructor of TargetNotFoundException. The message given to the constructor is returned by the method what that is inherited from the class exception. Thus, a catch block, such as the following one, can access the message:
catch(TargetNotFoundException except) {
cout << except.what() << endl;
}
If target has the value "glasses" when this block executes, the output is Target not found: glasses not found in a box!
However, there is a compilation error stating that:
error: invalid operands to binary expression ('const char [19]'
and 'const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::value_type *'
(aka 'const char *'))
: exception("Target not found: " + message.c_str()){ } // end constructor
~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
There's no + operator that takes two C strings as parameters. Simply remove .c_str() from your code
exception("Target not found: " + message){ }
Presumably just a typo from your text book.

Analyzing a simple C++ program with Frama-C

I started learning C++ from a great tutorial available at https://learnxinyminutes.com/docs/c++/ and would like to analyze in Frama-C a simplest example that shows references:
using namespace std;
#include <iostream>
#include <string>
int main() {
string foo = "I am foo";
string bar = "I am bar";
string& fooRef = foo; // This creates a reference to foo.
fooRef += ". Hi!"; // Modifies foo through the reference
cout << fooRef; // Prints "I am foo. Hi!"
// Doesn't reassign "fooRef". This is the same as "foo = bar", and
// foo == "I am bar"
// after this line.
cout << &fooRef << endl; //Prints the address of foo
fooRef = bar;
cout << &fooRef << endl; //Still prints the address of foo
cout << fooRef; // Prints "I am bar"
//The address of fooRef remains the same, i.e. it is still referring to foo.
return 0;
}
I compiled and installed Frama-C C++ plug-in called "Frama-Clang".
Now when I run frama-c I get warnings and errors in the output:
$ frama-c refs.cc
[kernel] Parsing FRAMAC_SHARE/libc/__fc_builtin_for_normalization.i (no preprocessing)
[kernel] Parsing refs.cc (external front-end)
refs.cc:13:17: warning: using directive refers to implicitly-defined namespace 'std'
using namespace std;
^
In file included from refs.cc:14:
In file included from /usr/share/frama-c/frama-clang/libc++/iostream:29:
/usr/share/frama-c/frama-clang/libc++/ostream:31:40: error: implicit instantiation of undefined template 'std::basic_ios<char, std::char_traits<char> >'
class basic_ostream : virtual public basic_ios<charT,traits> {
^
refs.cc:23:7: note: in instantiation of template class 'std::basic_ostream<char, std::char_traits<char> >' requested here
cout << fooRef; // Prints "I am foo. Hi!"
^
/usr/share/frama-c/frama-clang/libc++/iosfwd:37:68: note: template is declared here
template <class charT, class traits = char_traits<charT> > class basic_ios;
^
code generation aborted due to one compilation error
[kernel] user error: Failed to parse C++ file. See Clang messages for more information
[kernel] user error: stopping on file "refs.cc" that has errors.
[kernel] Frama-C aborted: invalid user input.
What is wrong?
(Frama-C is installed from a debian-testing repository in version 20170501+phosphorus+dfsg-2)
First of all, I'd like to point out the caveat on the Frama-Clang page:
Frama-Clang is currently in an early stage of development. It is known to be incomplete and comes without any bug-freeness guarantee.
Thus, if you're not already familiar with C++, I'd kindly suggest that starting right away with Frama-Clang might be a pretty big effort.
That said, the issue is, as mentioned in the comments, that STL support in Frama-Clang is minimal (in particular, the innocent-looking iostream is not exactly the easiest piece of code to handle when it comes to templates).
You might have better luck by using frama-c -cxx-nostdinc refs.cc, which will use your system's standard library instead of the one shipped with Frama-Clang: this will at least let clang type-check your code. There is however absolutely no guarantee that Frama-Clang itself will be able to understand all the constructions provided by this library.

Forcing a call to a class operator== to fail if return value is unused on GCC 4.4.7

I would like to know if there is a way to turn the use of a operator== on a class into a compiler error if the result is doing nothing with the result. The reason is to catch a use of the operator== when instead the developer intends to use operator=.
I do not care if the solution is GCC specific, as long as it works on GCC 4.4.7. Solutions can include GCC-specific attributes, compiler pragmas, or a source code file changes on the C++ class given below. Less attractive are changes to compiler options.
See example below. Uncomment the #define DO_STRING_LITERAL_TEST line below and you will see a helpful message from the compiler:
/usr/bin/g++ -MD -DDEBUG -g -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.cpp -c -o main.o
cc1plus: warnings being treated as errors
main.cpp: In function ‘int main(int, char**, char* const*)’:
main.cpp:37: error: comparison with string literal results in unspecified behaviour
Comment the #define DO_STRING_LITERAL_TEST line, but then uncomment the #define DO_STRING_CONTAINER_TEST line. This compiles just fine and I would like it to fail on the line indicated:
foo == " "; // <-- I need this to throw a compiler error
Here is the example code:
#include <iostream>
#include <string>
class StringContainer {
private:
std::string _val;
public:
StringContainer(const char * inString = NULL) : _val(inString ? inString : "") {}
bool operator==(const StringContainer & other) const
{
return _val == other._val;
}
void operator=(const char * other)
{
_val = other;
}
const char * getString() const
{
return _val.c_str();
}
};
int main(int argc, char *argv[], char *const envp[])
{
std::cout << __FILE__ << ":" << __LINE__ << ":" << "main begin" << std::endl;
#define DO_STRING_LITERAL_TEST
#ifdef DO_STRING_LITERAL_TEST
const char * invalidval = "foo bar";
// The following use of == throws a nice compiler error on GCC 4.4.7:
invalidval == " ";
#endif // DO_STRING_LITERAL_TEST
//#define DO_STRING_CONTAINER_TEST
#ifdef DO_STRING_CONTAINER_TEST
StringContainer foo;
foo = "some string";
foo == " "; // <-- I need this to throw a compiler error
std::cout << __FILE__ << ":" << __LINE__ << ":" << "foo contains <" << foo.getString() << ">" << std::endl;
#endif // DO_STRING_CONTAINER_TEST
std::cout << __FILE__ << ":" << __LINE__ << ":" << "main end" << std::endl;
return 0;
} // end main
One answer came very close is at https://stackoverflow.com/a/12416677/257924 . But that specifies use of a function attribute, which looks quite attractive, however the version of the GCC compiler that I am required to use balks at the use of -Werror=unused-result:
cc1plus: error: -Werror=unused-result: No option -Wunused-result
If there was a source-code-only change that temporarily (for some segment of code, perhaps) the unused-result warning, that would be even better. https://freeswitch.org/jira/browse/FS-6850#commentauthor_55299_verbose apparently indicates that "same test with gcc 4.8.3 passes ok, since it supports the param."
This shouldn't be something you verify in code. This should be something that your compiler will do for you. If you need a std::string, you should use a std::string - not a MyStringThatHasThisExtraEqualityCheck.
I'd suggest one of several things:
Bug your favorite gcc developer to get them to implement -Wunused-comparison as per this bug report
Just compile your code with clang, which does support that warning. You can then add -Werror to make it an error. You don't need to run the clang binary, but an extra orthogonal couldn't hurt (just gives you more excuse to slack off).
Use some external static analysis tool to verify this stuff for you.
Write unit tests.
GCC allows the __attribute_warn_unused_result__ on a function. Have you tried it with your class's comparator?
EDIT: Never mind. Skipped over the section where you say that your old GCC version still does not support that attribute.