I encountered a problem when using constexpr functions together with lambdas.
The following code is a minimal version which reproduces the error:
#include <iostream>
constexpr unsigned bar(unsigned q) {
return q;
}
template<unsigned N>
unsigned foo() {
return N;
}
template<typename F>
void print(F f) {
std::cout << f() << std::endl;
}
template<unsigned Q>
int stuff() {
constexpr unsigned n = bar(Q);
print([]() { return foo<n>(); });
}
int main() {
stuff<13>();
}
When compiling with gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) there are the following compiler errors:
constexpr_template.cpp: In lambda function:
constexpr_template.cpp:24:9: instantiated from ‘stuff() [with unsigned int Q = 13u]::<lambda()>’
constexpr_template.cpp:24:2: instantiated from ‘int stuff() [with unsigned int Q = 13u]’
constexpr_template.cpp:29:12: instantiated from here
constexpr_template.cpp:24:32: error: no matching function for call to ‘foo()’
constexpr_template.cpp:24:32: note: candidate is:
constexpr_template.cpp:9:10: note: template<unsigned int N> unsigned int foo()
Now the strange part is, if constexpr unsigned n = bar(Q); is changed into constexpr unsigned n = Q; it works.
What is also working is print([]() { return foo<bar(Q)>(); });...
Is this a bug in GCC or what am I doing wrong?
Gcc 4.6 was the first version to support constexpr, and it is not unusual for minor bugs to be present upon release of such features. You can verify from this Live Example on Coliru that gcc 4.8.1 and Clang 3.4 SVN correctly parse your code. You probably should upgrade your compiler accordingly.
Related
I have encountered a very weird issue when trying to compile this code:
#include <iostream>
struct A
{
int get()
{
return 5;
}
};
static void requestWork(A& program,size_t bytes)
{
}
template<typename T, typename... Args>
static void requestWork( A& program,size_t bytes,T t1, Args... args)
{
std::cout << t1 << "\n";
if (program.get() == bytes)
{
requestWork(program,bytes + sizeof(t1),args...);
}
else
{
requestWork(program,bytes + sizeof(t1), args...); //this line gets complaints
}
}
int main(int args, char* argsc[])
{
A a;
requestWork(a,0,0,0,0,0);
return 0;
}
This code doesn't actually do anything; it's a simplified version of some code I had written previously. For some reason, I get an error on the first requestWork recursive call:
recursively required from 'void requestWork(A&, size_t, T, Args ...) [with T = int; Args = {int, int}; size_t = long long unsigned int]'
I can't find anything online about this error and the weirdest part is that my code compiles fine; if I press build + run again (my IDE was Code::Blocks, I then migrated to CodeLite and ran into the same issue) the code runs fine. Anyone know anything about this?
I initially thought it was Code::Blocks being weird again but upon switching to CodeLite, I ran into the same issue, so I'm fairly certain it's a compiler issue. I'm using g++ 12.2.0. It came with my installation of Mingw-w64.
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.
The code below compiles on MSVC but fails on GCC (4.6.3). Why does it fail and what should I do to fix it?
#include <array>
class Foo {
public:
template<typename T, int N>
operator std::array<T, N>() const {
return std::array<T, N>();
}
};
int main(){
Foo val;
// both of the following lines fail on GCC with error:
// "no matching function call...", ultimately with a note:
// "template argument deduction/substitution failed"
auto a = val.operator std::array<int, 2>();
static_cast<std::array<int, 2>>(val);
return 0;
}
EDIT: The following code, however, does compile (on both compilers), despite passing in an int for std::array's template parameter.
template<int N, typename T>
struct Bar {
std::array<T, N> buf;
};
int main()
{
auto x = Bar<3, double>();
return 0;
}
If you read the full text of the error messages you get, the compiler is complaining because the type for N in your template class is int, while the second parameter of std::array is std::size_t, which is an unsigned long on your system.
Changing your template's declaration to use std::size_t N will fix the problem.
MSVC is not complaining possibly because it recognizes that the value "2" works for either case, or because of a compiler bug.
Consider the following code:
#include <cstddef>
#include <iostream>
#include <stdexcept>
class const_string {
public:
template <std::size_t sz>
constexpr const_string(const char (&str)[sz]): p_(str) {}
constexpr char operator[](std::size_t i) const { return p_[i]; }
private:
const char* p_;
};
template <char c>
void Print() { std::cout << c << '\n'; }
int main() {
constexpr char str[] = "Hello World";
Print<const_string(str)[0]>();
}
It compiles fine with clang, while GCC gives the following error message:
in constexpr expansion of 'const_string((* & str)).const_string::operator[](0ul)'
error: '(const char*)(& str)' is not a constant expression
However, if I change Print<const_string(str)[0]>(); to Print<const_string("Hello World")[0]>();. Both clang and GCC compile fine.
What is going on here? Which compiler is correct according to the standard?
It's a bug and appears to compile on gcc 5 as shown here.
I've found a strange behavior using polymorphic C++14 lambdas (lambdas with auto in their parameters):
Snippet 0:
#include <iostream>
template<typename T> void doLambda(T&& mFn)
{
std::forward<T>(mFn)(int{0});
}
template<typename T> void test(T&& mV)
{
doLambda([&mV](auto mE)
{
std::forward<decltype(mV)>(mV);
});
}
int main() { test(int{0}); return 0; }
clang++ 3.5.1: the snippet compiles and runs successfully.
g++ 4.9.2: the snippet fails to compile:
example.cpp: In instantiation of 'test(T&&)::<lambda(auto:1)> [with auto:1 = int; T = int]':
5 : required from 'void doLambda(T&&) [with T = test(T&&) [with T = int]::]'
13 : required from 'void test(T&&) [with T = int]'
18 : required from here
12 : error: 'mV' was not declared in this scope
std::forward<decltype(mV)>(mV);
^
Compilation failed
Snippet 1:
The only difference from snippet 0 is that the auto inside the lambda was replaced to int.
#include <iostream>
template<typename T> void doLambda(T&& mFn)
{
std::forward<T>(mFn)(int{0});
}
template<typename T> void test(T&& mV)
{
doLambda([&mV](int mE)
{
std::forward<decltype(mV)>(mV);
});
}
int main() { test(int{0}); return 0; }
clang++ 3.5.1: the snippet compiles and runs successfully.
g++ 4.9.2: the snippet compiles and runs successfully.
Snippet 3:
The lambda is now called in-place. auto is still used.
#include <iostream>
template<typename T> void test(T&& mV)
{
[&mV](auto mE)
{
std::forward<decltype(mV)>(mV);
}(int{0});
}
int main() { test(int{0}); return 0; }
clang++ 3.5.1: the snippet compiles and runs successfully.
g++ 4.9.2: the snippet compiles and runs successfully.
Why is g++ complaining about snippet 0? Is there anything wrong in my code? Is this a known bug or should I submit this?
As stated in the comments, this behavior is indeed a gcc bug.