I am developing a cross platform code base where the initial work is done using MS VC2010 compiler.Later I compile it on Linux with GCC (4.7).In many cases I am receiving :
"No matching function for call .." error in GCC.I noticed that it complains mostly when method params are non constant references.For example this:
void MyClass::DoSomeWork(ObjectSP &sprt, const std::string someName, const std::string anotherName, const std::string path, int index) {
sprt->GetProp()->Update(path, false);
}
Once I change the method to this:
void MyClass::DoSomeWork(const ObjectSP& sprt, const std::string& someName, const std::string& anotherName, const std::string& path, int index) {
sprt->GetProp()->Update(path, false);
}
GCC stops complaining.
Why does it happen and why does it not happen in VC compilers?
It's illegal to bind a non-const reference to a temporary. Historically however VS compilers have been less strict about this.
So if you have a function with a non-const reference and you call it with a temporary object (e.g. the return value from a function), g++ will compain, but VS won't. In this case g++ is right.
Always prefer const references if you can.
Related
// g++ --std=c++17 test.cpp -I /usr/local/include -L /usr/local/lib -lboost_system -Wall -pedantic -Wreturn-type -Wstrict-aliasing -Wreturn-local-addr -fsanitize=address -g
// LD_LIBRARY_PATH=/usr/local/lib ./a.out
#include <iostream>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
class A {
public:
fs::path path_;
const fs::path & path() const { return path_; }
fs::path & path() { return path_; }
};
class B {
public:
fs::path root_path_;
A path_2;
A path_3;
const fs::path & operator()() const {
for ( const auto & path : {
path_3.path(),
path_2.path(),
root_path_
}) {
if ( not path.empty() ) {
return path;
}
}
throw std::logic_error{"using default-constructed B"};
}
};
int main(int argc, char **argv) {
B b;
b.root_path_ = "foo/";
b.path_2.path() = "foo/bar";
b.path_3.path() = "foo/baz";
std::cout << b() << '\n';
return 0;
}
With the above code which, as best as I am aware, appears to be valid C++. Instead, when invoked, I get garbage output.
g++ does not initially complain, however Address Sanitizer does. g++ finally complains when adding -O2. The warning generated is
test.cpp: In member function ‘const boost::filesystem::path& B::operator()() const’:
test.cpp:31:12: warning: function may return address of local variable [-Wreturn-local-addr]
return path;
^~~~
test.cpp:29:3: note: declared here
}) {
^
Note that I am using:
$ cat /etc/fedora-release
Fedora release 25 (Twenty Five)
$ g++ --version
g++ (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Note that I solved the error by using a pointer instead.
const fs::path & operator()() const {
for ( const auto * path : {
&path_3.path(),
&path_2.path(),
&root_path_
}) {
if ( not path->empty() ) {
return *path;
}
}
throw std::logic_error{"using default-constructed B"};
}
But, that does leave some questions in my mind:
why does g++ not complain about the problem until -O2 is added?
exactly what about my code is undefined? I would say it's well-defined: B::operator() const is... well... const. That should mean that the objects used inside it are either locals or const members. It accesses const members. It constructs a local variable const auto & which... should reference a const member field. Exactly what causes it to bind to temporary instead?
A compiler is not obligated to issue a diagnostic for undefined behavior. If the compiler can detect code that's syntactically valid, but results in undefined behavior, and then complains about it, that's just icing on top of the cake. gcc's -O2 turns on additional optimizations, and does additional code analysis; as such gcc can sometimes detect undefined behavior only if optimizations are enabled.
It appears that your range iteration is over a temporary std::initializer_list. The range iteration variable is a reference into the initializer list. As such, the function ends up returning a reference to a temporary, which is what gcc is barking about, here. Since the temporary gets destroyed when the method returns, the method ends up returning a reference to a destroyed object. Any use of that reference comprises undefined behavior.
When you convert the temporary range to a list of pointers, you are iterating by value, and you are not returning a reference to a temporary, but rather derefencing the value in the range, which is a perfectly kosher pointer.
Pay attention to the following blurb from here:
The underlying array is not guaranteed to exist after the lifetime of
the original initializer list object has ended. The storage for
std::initializer_list is unspecified (i.e. it could be automatic,
temporary, or static read-only memory, depending on the situation).
The lifetime if the initializer list bound to the range iteration ends at the conclusion of the iteration. Which includes returning from the method. As such, the initializer list no longer exists, and you just returned a dangling reference.
I'm trying to understand what I am seeing while testing a change. The platform is openSUSE 42 with GCC 4.8, but it could affect others. The test code and error follows.
$ cat test.cxx
#include <string>
#if (__cplusplus >= 201103L)
# define STATIC_CONSTEXPR static constexpr
# define CONSTEXPR constexpr
#else
# define STATIC_CONSTEXPR static const
# define CONSTEXPR
#endif
struct Name
{
STATIC_CONSTEXPR char* GetName() {return "XXX";}
};
int main(int argc, char* arv[])
{
const char* name = Name::GetName();
return 0;
}
And:
$ g++ -O3 -std=c++11 test.cxx -o test.exe
test.cxx: In static member function ‘static constexpr char* Name::GetName()’:
test.cxx:13:44: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
STATIC_CONSTEXPR char* GetName() {return "XXX";}
^
Adding the following does not clear it:
struct Name
{
STATIC_CONSTEXPR char* const GetName() {return "XXX";}
};
And adding the following does clear it:
struct Name
{
STATIC_CONSTEXPR const char* GetName() {return "XXX";}
};
According to Does static constexpr variable make sense?:
Every variable declared constexpr is implicitly const but const and
static are almost orthogonal (except for the interaction with static
const integers.)
I thought I mostly understood constexpr but I'm obviously missing something (again). As I understand it, the C++ committee believes a value like "XXX" in the reproducer can somehow change after the file is saved even though its impossible under the laws of the physical universe as we currently understand them. However, to combat the problem, they gave us constexpr. An alternate explanation is here, but I have to admit I don't see the finer details that makes the difference.
Why am I seeing the warning, and why do I effectively need static constepr const to squash it?
$ uname -a
Linux opensuse-42 4.1.27-27-default #1 SMP PREEMPT Fri Jul 15 12:46:41 UTC 2016 (84ae57e) x86_64 x86_64 x86_64 GNU/Linux
opensuse-42:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib64/gcc/x86_64-suse-linux/4.8/lto-wrapper
Target: x86_64-suse-linux
Configured with: ../configure --prefix=/usr ... --host=x86_64-suse-linux
Thread model: posix
gcc version 4.8.5 (SUSE Linux)
constexpr on a function declaration means that the function is constexpr, not its return type.
So your problem really boils down to the following syntax being wrong:
char * foo = "bar";
Since C++11 this conversion from char const[] to char* is flat out illegal, and has been deprecated since C++98.
Making the pointer const, as opposed to pointer to const is explored more in-depth here.
So, to return a string literal you need to use char const*. static and constexpr are not related to this problem at all.
A string literator "..." is of type const char[], i.e. a array to a const characters. It can decay to const char* if needed.
You're getting the warning because you are using a deprecated conversion from const char* to char*/char* const. It doesn't matter that the function is constexpr, it only applies to the function itself, not the return value. It doesn't make sense to specify constexpr on the return value, because it is either already constexpr because of the function, or not.
So, your code is equivalent of writing
char* return_value = "XXX";
char* const return_value = "XXX";
Because none of those pointers point to a const char, you'll get a warning.
constexpr has nothing to do with it.
char *foo() { return "ABC"; }
will give the same warning. "ABC" is a string literal, and is of type const char[4] (array of four constant char). Like any other array, it will readily decay into a pointer of type char const * (pointer to constant char).
For string literals (only), there is also a deprecated conversion to char * (pointer to non-const char) - that is what your function is using, and that is what GCC doesn't like. The fix is either:
char const *foo() ...
or (equivalently)
const char *foo() ...
The following is nonsensical yet compiles cleanly with g++ -Wall -Wextra -Werror -Winit-self (I tested GCC 4.7.2 and 4.9.0):
#include <iostream>
#include <string>
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const std::string& str = str; // !!
std::cout << str << std::endl;
}
}
The line marked !! results in undefined behavior, yet is not diagnosed by GCC. However, commenting out the for line makes GCC complain:
error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]
I would like to know: why is GCC so easily fooled here? When the code is not in a loop, GCC knows that it is wrong. But put the same code in a simple loop and GCC doesn't understand anymore. This bothers me because we rely quite a lot on the compiler to notify us when we make silly mistakes in C++, yet it fails for a seemingly trivial case.
Bonus trivia:
If you change std::string to int and turn on optimization, GCC will diagnose the error even with the loop.
If you build the broken code with -O3, GCC literally calls the ostream insert function with a null pointer for the string argument. If you thought you were safe from null references if you didn't do any unsafe casting, think again.
I have filed a GCC bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 - I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
Unlike Clang, GCC doesn't have logic to detect self-initialized references, so getting a warning here relies on the code for detecting use of uninitialized variables, which is quite temperamental and unreliable (see Better Uninitialized Warnings for discussion).
With an int the compiler can figure out that you write an uninitialized int to the stream, but with a std::string there are apparently too many layers of abstraction between an expression of type std::string and getting the const char* it contains, and GCC fails to detect the problem.
e.g. GCC does give a warning for a simpler example with less code between the declaration and use of the variable, as long as you enable some optimization:
extern "C" int printf(const char*, ...);
struct string {
string() : data(99) { }
int data;
void print() const { printf("%d\n", data); }
};
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const string& str = str; // !!
str.print();
}
}
d.cc: In function ‘int main()’:
d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized]
void print() const { printf("%d\n", data); }
^
d.cc:13:19: note: ‘str’ was declared here
const string& str = str; // !!
^
I suspect this kind of missing diagnostic is only likely to affect a handful of diagnostics which rely on heuristics to detect problems. These would be the ones that give a warning of the form "may be used uninitialized" or "may violate strict aliasing rules", and probably the "array subscript is above array bounds" warning. Those warnings are not 100% accurate and "complicated" logic like loops(!) can cause the compiler to give up trying to analyse the code and fail to give a diagnostic.
IMHO the solution would be to add checking for self-initialized references at the point of initialization, and not rely on detecting it is uninitialized later when it gets used.
You claim it's undefined behavior, but when I compile the two cases to assembly, I definitely see the function-scoped variable not being initialized on the stack, and the block-scoped variable getting set to NULL.
That's as much of an answer as you're getting from me. I downloaded the C++ spec to definitively settle this, but fell into a Lovecraftian-type fugue when I gazed upon it, to preserve my fragile sanity...
I strongly suspect the block-scoped case is not actually undefined.
I know it has come up a zillion times but this time it was autogenerated code :
class testGetter{
testGetter * ptr; // wrote this
public:
// this is autogenerated by eclipse
const testGetter*& getPtr() const {
return ptr;
}
void setPtr(const testGetter*& ptr) {
this->ptr = ptr;
}
};
I am on windows 7 mingw - g++ version 4.7.0
So is it a bug in the templates of eclipse (juno) ?
EDIT : compiler invocation :
g++ -O0 -g3 -Wall -c -fmessage-length=0 -fpermissive -o Visitor.o "..\\Visitor.cpp"
EDIT 2013.06.12 : I should have added that I reported the thing after the feedback I got here
const testGetter*&
That represents a reference to a non-const pointer to a const testGetter. You cannot convert from a testGetter* to that type as that would break const-correctness in the following example:
const testGetter tmp;
testGetter t;
t.getPtr() = &tmp; // !!!
If the conversion was allowed the above code would compile, and after the line marked with the !!! the pointer stored inside t (which is of type testGetter*) would be pointing to a const testGetter, breaking const-correctness in the code.
You probably want to return just a pointer, not a reference to a pointer:
const testGetter* getPtr() const
Or alternatively add an extra const to guarantee const-correctness:
const testGetter *const& getPtr() const
In either case, the code is ensuring that you don't set the internal testGetter* to point to a const testGetter.
If the getter was autogenerated by the tool (Eclipse), then the tool has a bug or is a bit too simplistic to generate correct code. You will need to either create the getter manually or else fix the output of the generator.
Sidenote: Given a choice of Eclipse CDT or g++, I'd bet that the bug is in eclipse more often than not.
I'm still new to boost::bind, and now porting a program that was written 2 yrs ago in 2009, seeing the compile error below. Any idea to workaround would be appreciated.
Extracted cpp file:
class ClassA {
private:
cNamespace::Bounds bounds_msg_;
void boundsHandler(const PublisherPtr& p) {
p->publish(bounds_msg_);
}
void funcA() {
node_->advertise<cNamespace::Bounds>("bounds", 10,
boost::bind(&ClassA::boundsHandler, this, _1)); // <---- Line 445
}
};
Error upon CMake:
/home/userA/ClassA.cpp:445: instantiated from here
/usr/include/boost/bind/bind.hpp:313: error: no match for call to ‘(boost::_mfi::mf1<void, ClassA, const PublisherPtr&>) (ClassA*&, const ros::SingleSubscriberPublisher&)’
Environment: Ubuntu 10.10, g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
Might not be necessary but API reference of function advertise is here, or:
template<class M >
Publisher advertise (const std::string &topic,
uint32_t queue_size,
const SubscriberStatusCallback &connect_cb,
const SubscriberStatusCallback &disconnect_cb=SubscriberStatusCallback(),
const VoidConstPtr &tracked_object=VoidConstPtr(),
bool latch=false)
It looks like the function object that is produced by boost::bind is called with a different type than the function you bound.
i.e. it's called with const ros::SingleSubscriberPublisher& argument, instead of the expected const PublisherPtr& p.
Assuming SubscriberStatusCallback is a boost::function, you should make sure its argument matches the one you bound.