I want to test a recursion method and the following code has a compilation error. I've double checked the subscripts variables, and they should be correct. I really couldn't figure it out, can somebody help me?
#include <iostream>
#include <array>
using namespace std;
template <typename T, size_t Size>
void fn(array<T, Size>& data) {
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
if (data.size() > 1 ) {
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
fn(right);
}
}
int main() {
array<int, 5> test;
fn(test);
}
The compilation error is:
$ g++ -std=c++14 GuessNumber.cpp -o test
In file included from GuessNumber.cpp:2:0:
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/include/c++/array: In instantiation of ‘struct std::array<int, 2305843009213693952>’:
GuessNumber.cpp:15:9: recursively required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 2]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 5]’
GuessNumber.cpp:21:11: required from here
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/include/c++/array:94:12: error: size of type ‘std::array<int, 2305843009213693952>’ is too large (‘9223372036854775808’ bytes)
struct array
^~~~~
First of all, let us understand where the problem comes from.
The error you get
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/include/c++/array: In instantiation of ‘struct std::array<int, 2305843009213693952>’:
GuessNumber.cpp:15:9: recursively required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 2]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 5]’
tells you that you first instantiate fn with Size=5 (because you call it in the main), and this recursively instantiates fn with Size=2. Unfortunately the compiler does not show the full recursion, otherwise you would see that the recursion does not end here.
If you use in the program an array of size 2
array<int, 2> test;
you will see an additional level of recursion, in the error message:
GuessNumber.cpp:15:9: recursively required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 1]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 2]’
GuessNumber.cpp:21:11: required from here
which again, tells you that the instantiation of fn with Size=2 triggers the intantation of fn with Size=1. But the recursion does not end here. Try with
array<int, 1> test;
and you will finally see what is happpening:
/usr/include/c++/10.1.0/array: In instantiation of ‘struct std::__array_traits<int, 9223372036854775808>’:
/usr/include/c++/10.1.0/array:110:56: required from ‘struct std::array<int, 9223372036854775808>’
GuessNumber.cpp:13:33: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 0]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 1]’
GuessNumber.cpp:21:11: required from here
When Size=1, the compiler fully generates fn, including the code in the braces if (data.size() > 1). Even if this condition is always false, still the code is parsed and compiled. This means that, inside the never-executed-code, you instantiate fn with Size=0. But then, you have an overflow in the variable end, which attains a large value. Then the code after if instantiates an std::array with the exceedingly large size.
To fix this, you need to stop the compiler from generating code when Size=0.
This can be done in several ways.
With c++17 you have a very convenient if constexpr. If the condition of if constexpr is not true, the code is not instatiated at all, ending then the template recursion. So you can substitute
if (data.size() > 1 ) {
with
if constexpr (Size > 1 ) {
And compile with std=c++17. Notice that I changed the condition to Size > 1 because the data variable is not constexpr, hence you cannot use it at compile time.
If you do not have c++17, you can use SFINAE instead.
#include <iostream>
#include <array>
#include <type_traits>
using namespace std;
template <typename T, size_t Size>
typename std::enable_if<(Size == 0)>::type fn(array<T, Size>& data) { }
template <typename T, size_t Size>
typename std::enable_if<(Size > 0)>::type fn(array<T, Size>& data) {
const size_t begin{0};
const size_t end{Size-1}; // 1
const size_t leftUpper{(begin+end)/2}; // 0
const size_t rightLower{leftUpper+1}; // 1
if (data.size() > 1 ) {
array<T, end+1-rightLower> right; // 1
cout << "Right: " << end+1-rightLower << endl;
fn(right);
}
}
int main() {
array<int, 5> test;
fn(test);
}
The two approaches are perfectly equivalent, see here.
Try this:
#include <iostream>
#include <array>
using namespace std;
// the function contains its body just because looks like
// you want to implement some other logic there
template <typename T>
void fn(array<T, 2ul>& data) {
const size_t Size = 2;
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
}
template <typename T>
void fn(array<T, 1ul>& data) {
}
template <typename T>
void fn(array<T, 1ul>& data) {
const size_t Size = 1;
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
}
template <typename T, size_t Size>
void fn(array<T, Size>& data) {
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
if (data.size() > 1 ) {
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
fn(right);
}
}
int main() {
array<int, 5> test;
fn(test);
}
You code is not compiled conditionally. ifs do not work like you expect when you do template magic. One more example is here
Related
This is a follow-up to How to initialize std::array member in constructor initialization list when the size of the array is a template parameter. I got a great answer, but still I have an issue with g++/clang++ for my use case (program builds fine with msvc).
It looks like using the initializer list + index_sequence to initialize a std::array containing a class Hash with a deleted copy constructor fails, even if the initializer list does not contain the Hash class itself, but parameters to construct it. Unfortunately in my actual use case the copy constructor is not available because the class contains a mutex.
When building the following program, I have this error:
$ g++ -std=c++14 -c index_sequence.cxx
index_sequence.cxx: In instantiation of ‘A<H, N>::A(const std::vector<int>&, std::index_sequence<i ...>) [with long unsigned int ...i = {0ul, 1ul, 2ul, 3ul}; H = Hash; long unsigned int N = 4ul; std::index_sequence<i ...> = std::integer_sequence<long unsigned int, 0ul, 1ul, 2ul, 3ul>]’:
index_sequence.cxx:24:71: required from ‘A<H, N>::A(const std::vector<int>&) [with H = Hash; long unsigned int N = 4ul]’
index_sequence.cxx:37:22: required from here
index_sequence.cxx:28:86: error: use of deleted function ‘Hash::Hash(const Hash&)’
A(const vector<int> &data, std::index_sequence<i...>) : hashes{((void)i, data)...} {}
and here is the program:
#include <vector>
#include <array>
#include <utility>
using namespace std;
struct Hash {
const vector<int> &data;
Hash(const vector<int> &data)
: data(data) {
}
Hash(const Hash &) = delete;
uint64_t operator()(int id) const {
return data[id];
}
};
template <class H, size_t N>
class A {
public:
A(const vector<int> &data) : A(data, std::make_index_sequence<N>{}) {
}
template <std::size_t... i>
A(const vector<int> &data, std::index_sequence<i...>) : hashes{((void)i, data)...} {}
std::array<H, N> hashes;
};
int main () {
vector<int> data{1, 2, 3, 4};
A<Hash, 4> a{data};
}
Since the same program builds fine with msvc, I wonder if this is an issue with g++'s std::array initializer_list support?
I'm refactoring an API I've made a couple of years ago bringing a polymorphic implementation of common data structures (lists, maps, sets, trees, graphs), available at http://www.data-types.com. For structures requiring values comparison (eg: hash table), I've made them use C comparators and everything goes well.
In next version, however, I'm trying to level comparators' behavior so that users won't be required to create one when it can be inferred already. Everything is going fine until I try to convert a method argument into a template argument. Example code reduced to bare bone:
#include <iostream>
#include <cstddef>
#include <stdexcept>
template<typename VALUE>
class HashTable {
public:
HashTable(int (*comparator)(const VALUE&,const VALUE&), std::size_t (*hasher)(const VALUE&)){
std::cout << "HashTable constructor called" << std::endl;
}
bool contains(const VALUE& value, int (*customCompare)(const VALUE&,const VALUE&)) const{
return false;
}
~HashTable(){
std::cout << "HashTable destructor called" << std::endl;
}
};
template<typename _KEY, typename _VALUE>
struct MapEntry {
_KEY key;
_VALUE value;
};
template<typename KEY, typename VALUE, int (*comparator)(const KEY&, const KEY&)>
static inline int compareMapKey(const MapEntry<KEY,VALUE>& left, const MapEntry<KEY,VALUE>& right) {
return comparator(left.key, right.key);
}
template<typename KEY, typename VALUE, int (*comparator)(const VALUE&, const VALUE&)>
static inline int compareMapValue(const MapEntry<KEY,VALUE>& left, const MapEntry<KEY,VALUE>& right) {
return comparator(left.value, right.value);
}
template<typename KEY, typename VALUE, std::size_t (*hash)(const KEY&)>
static inline std::size_t hashMapKey(const MapEntry<KEY, VALUE>& element) {
return hash(element.key);
}
template<typename KEY, typename VALUE, int (*compareByKey)(const KEY&, const KEY&), std::size_t (*hashByKey)(const KEY&)>
class HashMap {
public:
HashMap(){
hashTable = new HashTable<MapEntry<KEY,VALUE>>(compareMapKey<KEY, VALUE, compareByKey>, hashMapKey<KEY, VALUE, hashByKey>);
std::cout << "HashMap constructor called" << std::endl;
}
bool containsValue(const VALUE& value, int (*comparator)(const VALUE&, const VALUE&)) const{
MapEntry<KEY,VALUE> mapEntry;
mapEntry.value = value;
return hashTable->contains(mapEntry, compareMapValue<KEY, VALUE, comparator>);
}
~HashMap(){
delete hashTable;
std::cout << "HashMap destructor called" << std::endl;
}
private:
HashTable<MapEntry<KEY,VALUE>>* hashTable;
};
static inline int comparator(const long& left, const long& right) {
if(left<right) return -1;
else if (left>right) return 1;
else return 0;
}
static inline std::size_t hash(const long& item) {
return item;
}
int main() {
long val = 1;
HashMap<long, long, comparator, hash> map;
map.containsValue(val, comparator);
std::cout << "!!!Hello World!!!" << std::endl;
return 0;
}
This refuses to compile, giving me this error:
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/Test.d" -MT"src/Test.o" -o "src/Test.o" "../src/Test.cpp"
../src/Test.cpp: In instantiation of ‘bool HashMap<KEY, VALUE, compareByKey, hashByKey>::containsValue(const VALUE&, int (*)(const VALUE&, const VALUE&)) const [with KEY = long int; VALUE = long int; int (* compareByKey)(const KEY&, const KEY&) = comparator; std::size_t (* hashByKey)(const KEY&) = hash]’:
../src/Test.cpp:78:35: required from here
../src/Test.cpp:53:79: error: no matching function for call to ‘HashTable<MapEntry<long int, long int> >::contains(MapEntry<long int, long int>&, <unresolved overloaded function type>)’ return hashTable->contains(mapEntry, compareMapValue<KEY, VALUE, comparator>);
../src/Test.cpp:12:7: note: candidate: bool HashTable<VALUE>::contains(const VALUE&, int (*)(const VALUE&, const VALUE&)) const [with VALUE = MapEntry<long int, long int>] bool contains(const VALUE& value, int (*customCompare)(const VALUE&,const VALUE&)) const{
../src/Test.cpp:12:7: note: no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘int (*)(const MapEntry<long int, long int>&, const MapEntry<long int, long int>&)’
Does anyone know for a solution of above? My C++ skills have become rustier since I'm no longer habitually working in that language...
A fundamental property of C++ templates, is that all template parameters must be resolved at compile time. That is, every template parameter must be known at compile time. The line with the compilation error:
return hashTable->contains(mapEntry,
compareMapValue<KEY, VALUE, comparator>);
But this comparator is a parameter to this function:
bool containsValue(const VALUE& value,
int (*comparator)(const VALUE&, const VALUE&);
Therefore, generally speaking: what this "comparator` is, is not known at compile time. The actual value gets passed in at runtime. This is the fundamental reason for your compilation error: all template parameters must be specified at compile time. Simply put, this overall approach that's used here simply won't work.
The following code does not compile with GCC 5.2 (C++14). It does compile with clang 3.6 (C++14). (original code can be found here)
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <utility>
template <typename T>
class aggregate_wrapper;
template <typename T, std::size_t n>
class aggregate_wrapper<T[n]> {
public:
using array = T[n];
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
aggregate_wrapper(const array& arr) {
std::copy(arr, arr + n, arr_);
}
aggregate_wrapper(array&& arr) {
std::move(arr, arr + n, arr_);
}
operator T* () {
return arr_;
}
operator const T* () const {
return arr_;
}
constexpr std::size_t size() const {
return n;
}
private:
array arr_;
};
int main() {
aggregate_wrapper<int[3]> arr;
static_assert(arr.size() == 3, "");
}
The error message produced is
main.cpp: In function 'int main()':
main.cpp:44:3: error: non-constant condition for static assertion
static_assert(arr.size() == 3, "");
^
main.cpp:44:25: error: call to non-constexpr function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]'
static_assert(arr.size() == 3, "");
^
main.cpp:34:25: note: 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not usable as a constexpr function because:
constexpr std::size_t size() const {
^
main.cpp:34:25: error: enclosing class of constexpr non-static member function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not a literal type
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not literal because:
class aggregate_wrapper<T[n]> {
^
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not an aggregate, does not have a trivial default constructor, and has no constexpr constructor that is not a copy or move constructor
Any ideas? Should the code compile according to the standard?
Or you could just make your existing variadic constructor serve as your constexpr constructor to perform the default construction:
template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
constexpr // <---- ADD THIS
aggregate_wrapper(Ts&&... xs)
: arr_{std::forward<Ts>(xs)...} {
// nop
}
In order for g++ to get it compiled you will need to add a default constructor:
aggregate_wrapper() = default;
please see it in action at: http://coliru.stacked-crooked.com/a/df1ac057960bebc7
I have the feeling that clang under the hood added it, but I am not 100% sure ...
GCC is wrong. Its diagnostic, in part, says:
main.cpp:34:25: note: '<...>' is not usable as a constexpr function because:
main.cpp:34:25: error: enclosing class of constexpr non-static member function '<...>' is not a literal type
... but there is no such rule. See [dcl.constexpr]/3 for the list of constraints that apply here.
You can work around the bogus GCC diagnostic by adding a dummy constexpr constructor (it's fine for that constructor to be private and/or deleted if you don't want any of your real constructors to be constexpr) or by making size be static.
I'm trying to create a template function that will return true if a value from an immutable array exists, and false otherwise. Here is the code:
#include <algorithm>
using std::find;
#include <vector>
using std::vector;
template<class T>
bool contains(const T *arr, int size, T val) {
vector<T> dest(arr, arr + size);
T *p = find(dest.begin(), dest.end(), val);
if (p == arr + size)
return false;
else
return true;
}
Every time I compile against a simple test program I get these errors:
main.cpp:10: instantiated from 'void testit(const T*, int, T) [with T = char]'
main.cpp:37: instantiated from here
main.cpp:69: error: cannot convert '__gnu_cxx::__normal_iterator<char*, std::vector<char, std::allocator<char> > >' to 'char*' in initialization
main.cpp: In function 'bool contains(const T*, int, T) [with T = int]':
main.cpp:10: instantiated from 'void testit(const T*, int, T) [with T = int]'
main.cpp:43: instantiated from here
main.cpp:69: error: cannot convert '__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >' to 'int*' in initialization
main.cpp: In function 'bool contains(const T*, int, T) [with T = std::string]':
main.cpp:10: instantiated from 'void testit(const T*, int, T) [with T = std::string]'
main.cpp:44: instantiated from here
(All of those errors are on the line that contains the find function)
What is the best way to go about this? Is the vector even neccessary?
Is the vector even neccessary?
No.
template<class T>
bool contains(const T *arr, int size, T val) {
return std::find(arr, arr + size, val) != arr + size;
}
If the argument is not an actual, complete array T.C.'s answer's solid (though taking the value by reference is a good idea), but if you only want to support entire arrays you can have something a smidge easier to use, with the array size extracted from the array's type:
template <class T, size_t N>
inline bool contains(const T(&arr)[N], const T& val)
{
return std::find(arr, arr + N, val) != arr + N;
}
Of course you can have both versions overloaded.
error: cannot convert '__gnu_cxx::__normal_iterator > >' to 'char*' in initialization
This is telling you the return type you have is not correct for std::find.
For the code you have, you should have something like the following.
template<class T>
bool contains(const T *arr, int size, T val)
{
vector<T> dest(arr, arr + size);
vector<T>::iterator p = find(dest.begin(), dest.end(), val);
return p != dest.end();
}
Is the vector even neccessary?
No, it could be simplified as follows.
template<class T>
bool contains(const T *arr, int size, T val)
{
const T* end = arr + size;
return find(arr, end, val) != end;
}
Why does the following not compile
#include <iostream>
template <typename T, size_t N>
const size_t len(T[N]){
return N;
}
int main(int argc, char* argv[]) {
using namespace std;
int arr[] = {1, 2, 3};
cout << len(arr);
}
but this does:
#include <iostream>
const size_t foo(int[3]) {
return 42;
}
int main(int argc, char* argv[]) {
using namespace std;
int arr[1] = {123};
cout << foo(arr);
}
but with obviously incorrect argument and strangely only with the parameter identifier omitted
I am using GCC 4.9.2 with -std=c++1y
edit:
the error message for the first example:
main.cpp:12:24: error: no matching function for call to 'len(int [3])'
cout << len(arr);
^
main.cpp:12:24: note: candidate is:
main.cpp:4:18: note: template<class T, unsigned int N> const size_t len(T*)
const size_t len(T[N]){
^
main.cpp:4:18: note: template argument deduction/substitution failed:
main.cpp:12:24: note: couldn't deduce template parameter 'N'
cout << len(arr);
^
A function argument that looks like an array is actually a pointer, for bizarre historical reasons. If you specify an array size, then it's ignored. So the template is equivalent to
template <typename T, size_t N>
const size_t len(T*){
return N;
}
The argument can be pointer or anything convertible to one, including an array of any size, so N cannot be deduced from the argument. You can fix this by taking the array by reference:
template <typename T, size_t N>
size_t len(T(&)[N]){
return N;
}
Now the argument can only be an array of known size, and N will be deduced from that size.