I have the following piece of code which represents an actual bigger piece of code:
#include <iostream>
using namespace std;
template<size_t N> class A {
public:
static constexpr size_t getN() {return N;}
};
template<size_t N> class B {
public:
void print() { cout << "B created: " << N << '\n';}
};
template <class T> class C {
public:
void set(T* a) {
t_ptr = a;
}
void create() {
constexpr int m = t_ptr->getN();
B<m> b;
b.print();
}
private:
T* t_ptr;
};
int main() {
constexpr int n = 2;
A<n> a;
C<A<n> > c;
c.set(&a);
c.create();
}
Compiling with g++ -o main main.cpp -std=c++11 and GCC/G++ 4.8.3 the expected output is received:
B created: 2
However, with GCC/G++ 4.9.1 the code does not compile, output:
main.cpp: In member function ‘void C<T>::create()’:
main.cpp:27:15: error: the value of ‘m’ is not usable in a constant expression
B<m> b;
^
main.cpp:26:27: note: ‘m’ used in its own initializer
constexpr int m = t_ptr->getN();
^
main.cpp:27:16: error: the value of ‘m’ is not usable in a constant expression
B<m> b;
^
main.cpp:26:27: note: ‘m’ used in its own initializer
constexpr int m = t_ptr->getN();
^
main.cpp:27:19: error: invalid type in declaration before ‘;’ token
B<m> b;
^
main.cpp:28:15: error: request for member ‘print’ in ‘b’, which is of non-class type ‘int’
b.print();
^
This is caused by a known bug in GCC 4.9: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59937 and in this older thread https://gcc.gnu.org/ml/gcc-bugs/2013-11/msg00067.html the usage of extern is proposed as a workaround. However, I am not able to get this workaround working.
Could you guys help me to make this code compile in GCC 4.9? Thank you!
Since this is not constexpr the access to this->t_ptr is not either.
clang's error is a bit more helpful
implicit use of 'this' pointer is only allowed within the
evaluation of a call to a 'constexpr' member function
Referring to:
N3690 5.19/2 (emphasis added)
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
— this, except in a constexpr function or a constexpr constructor that is being evaluated as
part of e;
Calling the static member function via the typename works
constexpr int m = T::getN();
Related
struct Outer
{
explicit constexpr Outer(int ii) : n(ii) {}
explicit constexpr Outer(double dd) : n(dd) {}
explicit constexpr Outer(double*) : Outer(3.1415926) {}
explicit constexpr Outer(int*): Outer(42) {}
template <typename R>
static
constexpr
Outer meow(R* r = nullptr) { return Outer(r); }
int as_i() const { return n.i; }
double as_d() const { return n.d; }
union Inner
{
int i;
double d;
constexpr Inner(int ii) : i(ii) {}
constexpr Inner(double dd) : d(dd) {}
~Inner () {}; // non-trivial destructor
};
Inner n;
};
//#include <type_traits>
//static_assert(std::is_literal_type_v<Outer>); // failed by both GCC and clang
int main(int argc, char**)
{
auto const o = Outer::meow(&argc);
return o.as_i();
}
The above code is compiled by GCC 12.1 but rejected by clang 14.0.0
https://godbolt.org/z/o6Tvrrdsv
clang (correctly, in my opinion) complains that
<source>:12:11: error: constexpr function's return type 'Outer' is not a literal type
Outer meow(R* r = nullptr) { return Outer(r); }
^
<source>:28:11: note: 'Outer' is not literal because it has data member 'n' of non-literal type 'Outer::Inner'
Inner n;
^
<source>:36:24: error: no matching function for call to 'meow'
auto const o = Outer::meow(&argc);
^~~~~~~~~~~
<source>:12:11: note: candidate template ignored: substitution failure [with R = int]
Outer meow(R* r = nullptr) { return Outer(r); }
^
Even though o is merely const, not constexpr, shouldn't this be an error? meow is trying to do something which shouoldn't be allowed.
(If I make o into constexpr, then GCC complains that Outer has a non-trivial destructor.)
Yes, the return type of a constexpr function must be a literal type and Outer is not. If a function definition doesn't satisfy the constexpr requirements, the program would be ill-formed.
However, you have a function template here. constexpr on a function template is allowed as long as there is at least one specialization which satisfies the requirements of a constexpr function. However, if this requirement is not fulfilled, the program isn't ill-formed. It is ill-formed, no diagnostic required (IFNDR).
There is no specialization here satisfying the requirements because you always have the Outer return type and so the program is IFNDR.
Both compilers are correct. They do not need to diagnose that the program is ill-formed.
Why this code snippet works with C++17 whereas the compiler complains when using C++11(i.e https://godbolt.org/z/71G91P)?
Are there any potential problems with this code snippet?
#include<iostream>
class ctx
{
public:
int map_create(void*){std::cout << "haha" << std::endl; return 0;};
};
ctx obj;
typedef int (ctx::*ctx_mem_func)(void*);
template <ctx_mem_func func>
int regHelper(void*)
{
((&obj)->*func)(nullptr);
return 0;
}
constexpr ctx_mem_func testFunc = &ctx::map_create;
typedef int(*callBackFunc)(void*);
int reg(callBackFunc)
{
return 0;
}
int main()
{
reg(regHelper<testFunc>);
//But this expression is ok.
reg(regHelper<&ctx::map_create>);
std::cout << "this is a test" << std::endl;
}
Here are the error messages when using c++11(gun 10.0.2):
<source>: In function 'int main()':
<source>:30:28: error: no matches converting function 'regHelper' to type 'callBackFunc {aka int (*)(void*)}'
reg(regHelper<testFunc>);
^
<source>:13:5: note: candidate is: template<int (ctx::* func)(void*)> int regHelper(void*)
int regHelper(void*)
^
This is a difference between C++14 and C++17. Simplified:
int f();
template<int (&)()> struct S {};
constexpr auto& q = f;
using R = S<q>; // valid in C++17, invalid in C++14
The change is to Allow constant evaluation for all non-type template arguments, meaning that now a constexpr variable naming a function (member function, etc.) is permissible as an NTTP where previously only the actual name of the function was permitted.
Consider the following example code:
#include <array>
struct MyClass
{
size_t value = 0;
constexpr static size_t size() noexcept
{
return 3;
}
};
template <size_t N>
void DoIt()
{
MyClass h;
std::array<int, h.size()> arr;
}
int main()
{
DoIt<1>();
}
When I try to compile this with GCC 7.3.0, I get an error about h not being usable in a non-constexpr context:
cexpr.cpp: In function ‘void DoIt()’:
cexpr.cpp:17:26: error: the value of ‘h’ is not usable in a constant expression
std::array<int, h.size()> arr;
^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
MyClass h;
^
cexpr.cpp:17:27: error: the value of ‘h’ is not usable in a constant expression
std::array<int, h.size()> arr;
^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
MyClass h;
^
However, when I try compiling the exact same code in Clang 6.0.0, it compiles without any errors. Additionally, when I modify the code to not be inside the templated DoIt() function, GCC compiles this just fine:
#include <array>
struct MyClass
{
size_t value = 0;
constexpr static size_t size() noexcept
{
return 3;
}
};
int main()
{
MyClass h;
// this compiles just fine in Clang and GCC
std::array<int, h.size()> arr;
}
I already know how to fix the first code so it compiles on GCC using decltype, but I'm curious to know why doesn't the first piece of code compile with GCC? Is this just a bug in GCC, or is there something I don't understand about using constexpr static member functions?
Looks like a bug to me.
The type and meaning of the expression h.size() is defined by [expr.ref] "Class member access":
[expr.post]/3
Abbreviating postfix-expression.id-expression as E1.E2, E1 is called the object expression. [...]
and
[expr.post]/6.3.1
If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.
(6.3.1) If it refers to a static member function and the type of E2 is “function of parameter-type-list returning T”, then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely “function of parameter-type-list returning T”.
This means h.size has the same type as ::MyClass::size and is evaluated as such, regardless of the fact that h is constexpr or not.
h.size() is then a call to a constexpr function and is a core constant expression according to [expr.const]/4.
This is a minimized part of Pointer to implementation code:
template<typename T>
class PImpl {
private:
T* m;
public:
template<typename A1>
PImpl(A1& a1) : m(new T(a1)) {
}
};
struct A{
struct AImpl;
PImpl<AImpl> me;
A();
};
struct A::AImpl{
const A* ppub;
AImpl(const A* ppub)
:ppub(ppub){}
};
A::A():me(this){}
A a;
int main (int, char**){
return 0;
}
It compilable on G++4.8 and prior and works as well. But G++4.9.2 compiler raise following errors:
prog.cpp: In constructor 'A::A()':
prog.cpp:24:15: error: no matching function for call to 'PImpl<A::AImpl>::PImpl(A*)'
A::A():me(this){}
^
prog.cpp:24:15: note: candidates are:
prog.cpp:9:5: note: PImpl<T>::PImpl(A1&) [with A1 = A*; T = A::AImpl]
> PImpl(A1& a1) : m(new T(a1)) {
^
prog.cpp:9:5: note: no known conversion for argument 1 from 'A*' to 'A*&'
prog.cpp:2:7: note: PImpl<A::AImpl>::PImpl(const PImpl<A::AImpl>&)
class PImpl {
^
prog.cpp:2:7: note: no known conversion for argument 1 from 'A*' to 'const PImpl<A::AImpl>&'
But it can be fixed by small hack. If i pass '&*this' instead of 'this' then it bring to compilable state.
Is it G++ regression or new C++ standards feature which eliminate backward compatibility?
We can make a simpler example that compiles on neither g++ 4.9 nor clang:
template <typename T>
void call(T& ) { }
struct A {
void foo() { call(this); }
};
int main()
{
A().foo();
}
That is because this is, from the standard, [class.this] (§9.3.2):
In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value
is the address of the object for which the function is called.
You cannot take an lvalue reference to a prvalue, hence the error - which gcc explains better than clang in this case:
error: invalid initialization of non-const reference of type A*& from an rvalue of type A*
If we rewrite call to either take a const T& or a T&&, both compilers accept the code.
This didn't compile for me with gcc-4.6, so it seems that gcc-4.8 is where the regression occurred. It seems what you want is to take A1 by universal reference, ie: PImpl(A1 && a1). This compiles for me with both gcc-4.6, gcc-4.8 and gcc-4.9.
Consider the following code:
#include <iostream>
#include <type_traits>
template<typename Type>
class Test
{
public:
constexpr Test(const Type val) : _value(val) {}
constexpr Type get() const {return _value;}
static void test()
{
static constexpr Test<int> x(42);
std::integral_constant<int, x.get()> i;
std::cout<<i<<std::endl;
}
protected:
Type _value;
};
int main(int argc, char *argv[])
{
Test<double>::test();
return 0;
}
Under g++ 4.7.1, it returns the error:
main.cpp: In static member function ‘static void Test<Type>::test()’:
main.cpp:13:48: error: invalid use of ‘Test<Type>::get<int>’ to form a pointer-to-member-function
main.cpp:13:48: note: a qualified-id is required
main.cpp:13:48: error: could not convert template argument ‘x.Test<Type>::get<int>()’ to ‘int’
main.cpp:13:51: error: invalid type in declaration before ‘;’ token
I do not understand the problem: is it a compiler bug or is it a real problem ?
How to solve it ?
It looks like a GCC bug, clang 3.2 compiles without any error