I can't seem to figure out why the following code doesn't work:
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(const std::array<int, s>& arr) {
a<arr.size()>(); // error: no matching function for call to 'a'
}
int main() {
const std::array<int, 2> arr {{0, 0}};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
GCC fails with the following:
test.cpp: In instantiation of ‘void b(const std::array<int, s>&) [with long unsigned int s = 2]’:
test.cpp:13:22: required from here
test.cpp:6:18: error: no matching function for call to ‘a<(& arr)->std::array<int, 2>::size()>()’
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~~~~~~^~
test.cpp:3:37: note: candidate: ‘template<long unsigned int s> void a()’
3 | template <long unsigned int s> void a() {}
| ^
test.cpp:3:37: note: template argument deduction/substitution failed:
test.cpp:6:18: error: ‘arr’ is not a constant expression
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~~~~~~^~
test.cpp:6:15: note: in template argument for type ‘long unsigned int’
6 | a<arr.size()>(); // Doesn't
| ~~~~~~~~^~
I assume the ‘arr’ is not a constant expression
part is most relevant, but I don't understand why the same line works in main()
(is it a constant expression there?), and why passing arr
as a const
copy (rather than a reference) also resolves the issue.
PS: I know that I can just use a<s>();
, but I'm just trying to figure out what this error means.
Maybe in C++2X, it will work with consteval
functitons.
For non-reference argument it works https://godbolt.org/z/Wx9va54z5.
I think it is fine to pass std::array
of built-ins by value in general anyway.
(This works in GCC and clang.)
#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(std::array<int, s> arr) {
a<arr.size()>(); // ok
}
int main() {
const std::array<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
static
and constexpr
size()
member function in std::array
would work(!), even for const&
argument. https://godbolt.org/z/anP1e9qErCORRECTION: This is correct only for GCC, in clang doesn't work https://godbolt.org/z/65d5G9Yfo , Thanks @IlCapitano
#include <array>
template<class T, std::size_t D>
struct MyArray{
static constexpr std::size_t size(){return D;}
};
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(MyArray<int, s> const& arr) {
a<arr.size()>(); // ok
}
int main() {
const MyArray<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
which makes even more puzzling why std::array
doesn't have a static (and constexpr) size
member.
2.5) CORRECTION: This is correct only for GCC, in clang doesn't work https://godbolt.org/z/65d5G9Yfo , Thanks @IlCapitano
In clang, a static funciton works but not passing the instance, which kind of defeats the purpose:
https://godbolt.org/z/En13aEWPz
#include <array>
template<class T, std::size_t D>
struct MyArray{
static constexpr std::size_t size(){return D;}
};
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(MyArray<int, s> const& arr) {
a<std::decay_t<decltype(arr)>::size()>(); // error: no matching function for call to 'a'
}
int main() {
const MyArray<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}
std::tuple_size<decltype(...)>
(or s
itself) is a good workaround https://godbolt.org/z/K9xxKa1Px#include <array>
template <long unsigned int s> void a() {}
template <long unsigned int s> void b(std::array<int, s> arr) {
a<std::tuple_size<decltype(arr)>::value /*or just s*/>(); // ok
}
int main() {
const std::array<int, 2> arr = {};
a<arr.size()>(); // Works
b<arr.size()>(arr);
return 0;
}