Sample code:
#include <string>
#include <set>
using namespace std;
class x
{
private:
int i;
public:
int get_i() const { return i; }
};
struct x_cmp
{
bool operator()(x const & m1, x const & m2)
#if _MSC_VER
const
#endif
{
return m1.get_i() > m2.get_i();
}
};
std::set<x, x_cmp> members;
void add_member(x const & member)
{
members.insert(member);
}
Invocations:
$ g++ -c -std=c++14 -pedantic -Wall -Wextra
<nothing>
$ clang++ -c -std=c++14 -pedantic -Wall -Wextra
<nothing>
$ icc -c -std=c++14 -pedantic -Wall -Wextra
<nothing>
$ cl /c /std:c++14 /Za
<nothing>
Question: why msvc requires const while others don't? Or why others don't require const?
This is LWG2542. In C++14, the wording for the comparison operator said "possibly const", which was interpreted by GCC and Clang as meaning that the comparison operator was not required to be const qualified. MSVC always required it.
This is a wording defect, as comparison operators for associative containers should be const qualified. This was changed in C++17 to require the comparator to be const. This is a breaking change, so valid (though broken) C++14 code may fail to compile in C++17.
Related
Sample code:
#include <string>
#include <set>
using namespace std;
class x
{
private:
int i;
public:
int get_i() const { return i; }
};
struct x_cmp
{
bool operator()(x const & m1, x const & m2)
#if _MSC_VER
const
#endif
{
return m1.get_i() > m2.get_i();
}
};
std::set<x, x_cmp> members;
void add_member(x const & member)
{
members.insert(member);
}
Invocations:
$ g++ -c -std=c++14 -pedantic -Wall -Wextra
<nothing>
$ clang++ -c -std=c++14 -pedantic -Wall -Wextra
<nothing>
$ icc -c -std=c++14 -pedantic -Wall -Wextra
<nothing>
$ cl /c /std:c++14 /Za
<nothing>
Question: why msvc requires const while others don't? Or why others don't require const?
This is LWG2542. In C++14, the wording for the comparison operator said "possibly const", which was interpreted by GCC and Clang as meaning that the comparison operator was not required to be const qualified. MSVC always required it.
This is a wording defect, as comparison operators for associative containers should be const qualified. This was changed in C++17 to require the comparator to be const. This is a breaking change, so valid (though broken) C++14 code may fail to compile in C++17.
I found a difference in behavior between gcc and clang when compiling BoringSSL, and was able to whittle it down to the following test case to illustrate:
typedef char *OPENSSL_STRING;
#if USE_TYPEDEF
#define constptr const OPENSSL_STRING
#else
#define constptr const char *
#endif
int
foo (const void **ap)
{
constptr a = (constptr) *ap;
return a != 0;
}
I tested four scenarios as follows:
sh$ g++ -c t2.cc -Wignored-qualifiers -DUSE_TYPEDEF
t2.cc: In function ‘int foo(const void**)’:
t2.cc:11:30: warning: type qualifiers ignored on cast result type [-Wignored-qualifiers]
11 | constptr a = (constptr) *ap;
| ^~
sh$ g++ -c t2.cc -Wignored-qualifiers
sh$ clang++ -c t2.cc -Wignored-qualifiers -DUSE_TYPEDEF
sh$ clang++ -c t2.cc -Wignored-qualifiers
sh$
Is this a bug in gcc -- or is there something more going on that I don't understand?
For reference: the warning is in BoringSSL's stack.h
Given const OPENSSL_STRING, const is qualified on the typedef OPENSSL_STRING itself, so the type would be char * const, i.e. const pointer to non-const char (note that it's not const char *). Gcc is just trying to tell you that as the cast result the const part is ignored. i.e. (char * const) *ap; has the same effect as (char *) *ap;.
Changing the type to int might be clearer.
const int i = (int) 0; // a weird conversion
const int i = (const int) 0; // same effect as above
So, this is eating me for the last two days:
I can't link my application, depending on which compiler I use and what optimizations levels are used:
gcc plays nicely with -O1, -O2, -O3 but fails with -O0 and
clang plays nicely with -O2 and -O3 and fails with -O1 and -O0.
There are a bunch of horrible templates in it, but I see no reason for this obscure behaviour.
Here is a minimal amount of code that reproduces the problem:
#include <map>
#include <memory>
#include <iostream>
using id_type = long;
class CB : public std::enable_shared_from_this<CB>
{
public:
CB() = default;
virtual ~CB() = default;
};
template<class F, class C> class SC : public CB
{
};
class FB
{
public:
virtual ~FB() = default;
template<class T, class B> T* as(B* v) const { return dynamic_cast<T*>(v);}
};
template<class T>
class F : public FB
{
public:
virtual std::shared_ptr<CB> create() const
{
auto n = std::make_shared<T>();
return n;
}
};
struct B
{
virtual ~B() = default;
static const id_type ID = 1;
};
class A : virtual public B, virtual public SC<A, B>
{
public:
A() = default;
};
static std::map<id_type, std::shared_ptr<FB>> crtrs {
{A::ID, std::make_shared<F<A>>()}
};
int main()
{
std::cout << crtrs.size();
}
Here is the same online https://gcc.godbolt.org/z/sb9b5E
And here are the error messages:
fld#flap ~/work/p/test1
> $ g++ -O1 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O2 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O3 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O4 main.cpp
fld#flap ~/work/p/test1
> $ g++ -O0 main.cpp
/tmp/cc8D7sNK.o: In function `__static_initialization_and_destruction_0(int, int)':
main.cpp:(.text+0x1c0): undefined reference to `B::ID'
collect2: error: ld returned 1 exit status
fld#flap ~/work/p/test1
> $ clang++ -O0 main.cpp
/tmp/main-c49b32.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x7a): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld#flap ~/work/p/test1
> $ clang++ -O1 main.cpp
/tmp/main-cf18ee.o: In function `__cxx_global_var_init.1':
main.cpp:(.text.startup+0x3c): undefined reference to `B::ID'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
fld#flap ~/work/p/test1
> $ clang++ -O2 main.cpp
fld#flap ~/work/p/test1
> $ clang++ -O3 main.cpp
If someone has any ideas what might be the reason, any hints are more than welcome.
You did not provide a definition of B::ID anywhere. It just happens that with higher optimization all accesses happened to be elided by the compiler.
You need to add the definition of a static member at toplevel scope:
const id_type B::ID;
If a static const data member is only read, it does not need a separate definition because compile time constants are not considered ODR-used (One Definition Rule).
The reason that you need the definition at all is that the map constructor expects std::map::value_type in the initializer list which is std::pair<const Key, T>. The constructor that gets picked in this case is pair( const T1& x, const T2& y );
In order to call this constructor the address of A::ID which is B::ID gets taken, which constitutes an ODR-use even for a constant.
Because the pair constructor is almost trivial in this case it gets inlined at higher optimization and the only reference to &B::ID disappears because B::ID's value is known and the pair can be directly initialized.
See also: static
If you are using C++17 or newer, you can also make B:ID constexpr instead of const, then you do not need a separate definition, because constexpr is implicitly inline (inline static const should also be OK).
Given this code:
template <typename>
struct Check { constexpr static bool value = true; };
struct A {
A() noexcept(Check<int>::value) = default;
};
Compiling with various versions of GNU g++ works fine, but it always fails with clang++ 5.0.1 (both with libstdc++ and libc++):
$ g++-4.9.4 test.cpp -c -o test -std=c++11 -Wall -Wextra
$ g++-5.4.0 test.cpp -c -o test -std=c++11 -Wall -Wextra
$ g++-6.4.0 test.cpp -c -o test -std=c++11 -Wall -Wextra
$ g++-7.2.0 test.cpp -c -o test -std=c++11 -Wall -Wextra
$ clang++ test.cpp -c -o test -std=c++11 -Wall -Wextra -Weverything
test.cpp:2:16: warning: 'constexpr' specifier is incompatible with C++98 [-Wc++98-compat]
struct Check { constexpr static bool value = true; };
^
test.cpp:5:39: warning: defaulted function definitions are incompatible with C++98 [-Wc++98-compat]
A() noexcept(Check<int>::value) = default;
^
test.cpp:5:9: warning: noexcept specifications are incompatible with C++98 [-Wc++98-compat]
A() noexcept(Check<int>::value) = default;
^
test.cpp:5:5: error: exception specification is not available until end of class definition
A() noexcept(Check<int>::value) = default;
^
test.cpp:5:18: note: in instantiation of template class 'Check<int>' requested here
A() noexcept(Check<int>::value) = default;
^
3 warnings and 1 error generated.
$ clang++ test.cpp -c -o test -std=c++11 -Wall -Wextra
test.cpp:5:5: error: exception specification is not available until end of class definition
A() noexcept(Check<int>::value) = default;
^
test.cpp:5:18: note: in instantiation of template class 'Check<int>' requested here
A() noexcept(Check<int>::value) = default;
^
1 error generated.
On Compiler Explorer this also seems to work for all versions of GCC newer 4.7.0 (including trunk), but fails for all Clang versions except for Clang 3.4.0, 3.5.0, 3.5.1 and trunk. So this seems like Clang bug.
Is it possible to work around this bug? How? Is this bug already tracked somewhere?
EDIT:
I tracked this bug down to Clang PR23383. As of now there is no notice about this being fixed in Clang although it seems to work with Clang trunk in Compiler Explorer.
This might be related to PR30860 (and C++ DR1330 as described in Richard Smith's comment on PR30860). It seems that the issue has something to do about when the contents of the noexcept() are parsed.
And so it turns out that one obvious workaround is to try to force the compiler to parse these contents elsewhere. One possible solution would be to provide the contents noexcept() as a constexpr member constant:
template <typename>
struct Check { constexpr static bool value = true; };
struct A {
A() noexcept(workaround) = default;
private: /* Work around Clang PR23383: */
static constexpr bool workaround = Check<int>::value;
};
Unfortunately, this does not work in all cases, e.g. such as this:
#include <type_traits>
struct Base { virtual ~Base() noexcept; };
struct OuterClass {
class InnerDerived: public Base {
private:
static constexpr auto const workaround =
std::is_nothrow_default_constructible<Base>::value;
public:
InnerDerived() noexcept(workaround);
};
class InnerDerived2: public InnerDerived {
private:
static constexpr auto const workaround2 =
std::is_nothrow_default_constructible<InnerDerived>::value;
public:
InnerDerived2() noexcept(workaround2);
};
};
The latter yields the error even when using Clang trunk in Compiler Explorer. Any ideas why?
I have the following constructor of an object
Segment::Segment(QPointF const& start, QPointF const& end):
mOrigin(toVector3df(start)),mEnd(toVector3df(end)){
}
mOrigin is of type Vector3df and the function toVector3df(QPointF const&) returns a temporary Vector3df. So far so good. The code compiles fine and works like a charm under linux, gcc 4.4.3. most warnings activated.
Now I wanted to cross-compile the same code for a Nokia Smartphone (Meamo Fremantle)
and all of a sudden I get very weird compiler warnings:
include/vector3d.h: In constructor 'Segment::Segment(const QPointF&, const QPointF&)':
include/vector3d.h:64: warning: 'this.902' is used uninitialized in this function
include/vector3d.h:64: note: 'this.902' was declared here
First: Of course there is no real variable called this.902 inside 'Vecto3df' so my first question would be: "Has anyone seen some warning like this ?" Further there is nothing wrong with Vector3df constructors, they are very simple and toVector3df(QPointF const&) is a one liner non-member template function that works perfect in other parts of the code.
Vector3df inherits from a template that only defines non-member functions, no variables no, virtual functions.
Second, when I change the above code to the following
Segment::Segment(QPointF const& start, QPointF const& end):
mOrigin(),mEnd(){
mOrigin = toVector3df(start);
mEnd = toVector3df(end);
}
The code works fine without any warnings.
So what am I missing here ? Has anybody an idea what the origin of the warnings could be. Am I violating some doctrine I'm unaware of. Is the fremantle compiler (Maemo 5, Qt 4.6.2) more severe or buggy ?
Thanks in advance, Martin
Edit:
Here is a minimal example, sorry for the length :-P
#include <iostream>
#include <sstream>
#include <QPoint>
template<typename T> class IoEnabled {};
template<typename T>
class Vector3d: public IoEnabled<Vector3d<T> > {
private:
T mX; T mY; T mZ;
public:
Vector3d(T const& x, T const& y, T const& z=0.0) : mX(x), mY(y), mZ(z) {}
};
typedef Vector3d<float> Vector3df;
template<class T>
Vector3df toVector3df(T const& p){
return Vector3df(p.x(),p.y(),0.0);
}
class Segment {
private:
Vector3df mOrigin; Vector3df mEnd;
public:
Segment(QPointF const& start, QPointF const& end):
mOrigin(toVector3df(start)),mEnd(toVector3df(end)){
//if toVector3df(...) is moved from the initializer to the body it works
}
};
int main(int argc, char **argv) {
(void) argc; (void) argv;
Segment temp(QPointF(1,2),QPointF(3,4));
return 0;
}
Compiler call:
g++ -c -pipe -Werror -Wall -Wextra -Wunused -Wundef -Wpointer-arith -Wcast-align -Wwrite-strings -Wredundant-decls -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -D_REENTRANT -Wall -W -DQT_GL_NO_SCISSOR_TEST -DQT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH=1024 -DMAEMO -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/opt/QtSDK/Maemo/4.6.2/sysroots/fremantle-arm-sysroot-20.2010.36-2-slim/usr/share/qt4/mkspecs/linux-g++-maemo5 -I. -I/opt/QtSDK/Maemo/4.6.2/sysroots/fremantle-arm-sysroot-20.2010.36-2-slim/usr/include/QtCore -I/opt/QtSDK/Maemo/4.6.2/sysroots/fremantle-arm-sysroot-20.2010.36-2-slim/usr/include/QtGui -I/opt/QtSDK/Maemo/4.6.2/sysroots/fremantle-arm-sysroot-20.2010.36-2-slim/usr/include -Isrc -Irelease/moc -o release/obj/main.o src/main.cpp
The Template inheritance seems to be crucial, if the Vector3d does not inherit everything works fine.
There is nothing wrong in using functions returning a temporary in member initializer lists.
Even the order in which the members will be inialized is well defined in the standard.